Boost WordPress performance with Varnish Cache

Lets face it, WordPress is slow. With every request it has to go though thousands lines of code and multiple SQL queries to render a page. Very popular configuration for a WordPress site is Apache, mod_rewrite, mod_php, PHP and MySQL. It’s very good setup but can’t be consider the fastest (at least without any additional tweaking).

The good news is WordPress doesn’t have to be a speed demon. In most cases it’s just a CMS to produce static pages. If the content is static it doesn’t make any sense to waste CPU cycles on re-rendering the same HTML over and over again.



Currently I’m running this blog on Amazon EC2 Micro instance. It’s the smallest (and slowest) setup you can get from Amazon. The micro instance has 613 MB of RAM and a limited access to CPU. When I tried to benchmark it with the Apache Benchamark I got about 1 req/sec… and crashed database. Ten concurrent connections were enough to DoS my blog! Fifteen minutes later which was required to setup Varnish Cache the same hardware could handle 665 req/sec. During the test CPU got to 65% which means I reached broadband limits.

$ ab -c300 -n500 https://systemsarchitect.net/

Server Software:        Apache/2.2.22
Server Hostname:        systemsarchitect.net
Server Port:            80

Document Path:          /
Document Length:        128174 bytes

Concurrency Level:      300
Time taken for tests:   0.751 seconds
Complete requests:      500
Failed requests:        0
Write errors:           0
Total transferred:      64310000 bytes
HTML transferred:       64087000 bytes
Requests per second:    665.85 [#/sec] (mean)
Time per request:       450.555 [ms] (mean)
Time per request:       1.502 [ms] (mean, across all concurrent requests)
Transfer rate:          83633.83 [Kbytes/sec] received

So why to make a website fast? There are at least 3 reasons:

– It will survive sudden traffic spikes (so called Slashdot effect)

– better Google ranking

– according to Google’s research there is a correlation between website’s response time and consumed content. In other words the faster website is the higher chance for another click.

Varnish Cache is a web application accelerator also known as a caching HTTP reverse proxy. You install it in front of any server that speaks HTTP and configure it to cache the contents. Varnish Cache is really, really fast. It typically speeds up delivery with a factor of 300 – 1000x, depending on your architecture.

Varnish stands in front of a web server which means it will have to listen on port 80. By default this port it already taken by Apache. The first thing is to change configuration of your web server.

$ vim /etc/apache2/ports.conf

Change port 80 to 8080.

NameVirtualHost *:8080
Listen 8080

    Listen 443


    Listen 443

Edit virtual host settings.

$ vim /etc/apache2/sites-available/architect

Alter port from 80 to 8080.


Now install varnish. Depends on your distribution you might get different version of the software. This article is created for varnish 3.x.

$ apt-get install varnish
$ varnishd -V
varnishd (varnish-3.0.2 revision cbf1284)
Copyright (c) 2006 Verdens Gang AS
Copyright (c) 2006-2011 Varnish Software AS

Edit varnish settings and set port and cache size.

$ vim /etc/default/varnish

Memory on my server is limited so I use only 64 MB. Default value is 254 but quoter of that is enough for a small blog.

DAEMON_OPTS="-a :80 
                              -T localhost:6082 
                              -f /etc/varnish/default.vcl 
                              -S /etc/varnish/secret 
                              -s malloc,64m"

Edit varnish configuration.

$ vim /etc/varnish/default.vcl

Change the VCL script to

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
    if (req.restarts == 0) {
        if (req.http.x-forwarded-for) {
            set req.http.X-Forwarded-For =
            req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }
    if (req.request == "PURGE") {
        if ( client.ip != "54.246.44.13") {
            error 405 "Not allowed.";
        }
        return (lookup);
    }
    if (req.request != "GET" &&
        req.request != "HEAD" &&
        req.request != "PUT" && 
        req.request != "POST" &&
        req.request != "TRACE" &&
        req.request != "OPTIONS" &&
        req.request != "DELETE") {
            return (pipe);
    }
    if (req.request != "GET" && req.request != "HEAD") {
        return (pass);
    }
    if (!(req.url ~ "wp-(login|admin)") &&
        !(req.url ~ "&preview=true" ) ) {
        unset req.http.cookie;
    }

    if (req.http.Authorization || req.http.Cookie) {
        return (pass);
    }
    return (lookup);
}

sub vcl_hit {
    if (req.request == "PURGE") {
        purge;
        error 200 "Purged.";
    }
    return (deliver);
}

sub vcl_miss {
    if (req.request == "PURGE") {
        purge;
        error 200 "Purged.";
    }
    return (fetch);
}

sub vcl_fetch {
    if (!(req.url ~ "wp-(login|admin)")) {
        unset beresp.http.set-cookie;
        set beresp.ttl = 96h;
    }

    if (beresp.ttl <= 0s ||
        beresp.http.Set-Cookie ||
        beresp.http.Vary == "*") {
            set beresp.ttl = 120 s;
            return (hit_for_pass);
    }
    return (deliver);
}

You will have to change the IP 54.246.44.13 to your server’s address.

Everything is in place now so the last thing to do is restartng services.

$ sudo /etc/init.d/apache2 restart
$ sudo /etc/init.d/varnish restart

To make sure caching is working run tail against all apache logs and refresh your website few times.

$ tail -f /var/log/apache2/*

First request will hit Apache server and you should see new entries in the log. Varnish will put all resources for that URL in cache. Every following request shouldn’t populate any new logs.

The VCL configuration makes Varnish cache every GET request for 96 hours. There is an exception for wp-login and wp-admin. Those are dynamic pages and it’s better not to cache them. Your visitors have no reason to go there anyway.

Varnish will cache everything for 96h. If you create a new post or edit an existing one your changes won’t be visible. Varnish doesn’t know something has changed and will continue serving stale content. You can restart the service to wipe all data.

$ /etc/init.d/varnish restart

It’s not very user friendly approach. It also clears more then it should. Don’t worry, it’s WordPress. There is plugin for everything. Install “Varnish HTTP Purge” extension. No configuration is required. Default.vcl file already handlers PURGE requests. Now you can visit your blog, open network tab in Firebug and enjoy response time below 200ms.

Varnish is an amazing peace of software with very powerful features. It can act as a load balancer, it can pull different parts of your website from different palaces (ESI) and if it can’t do something there might be an extension for that. There is nothing to wait for. Download it. Use it.

You might like to read

Practical Load Balancing: Ride the Performance Tiger