Varnish config for WordPress

The server that hosts this website was configured from the ground up by myself. The purpose was to configure a server with a rather limited resource set that was able to serve a few websites without handing in on performance and page speed.

One of the ways to maintain a good performance of the server and websites is to ease the web server from its tasks by implementing a caching proxy. In this case I used Varnish Cache to accelerate my websites.

Installation

First of all I created a new entry in my Debian apt sources to be able to install the latests stable release of Varnish:

~$ cat /etc/apt/sources.list.d/varnish.list
deb http://repo.varnish-cache.org/debian/ wheezy varnish-3.0
~$ apt-get update
~$ apt-get install varnish

Configuration

Next on was the creation of the Varnish configuration itself. This configuration is optimised for WordPress powered websites.

This is the content of my /etc/varnish/default.vcl (a download link to this file can be found at the bottom of this page):

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}
 
# Who is allowed to purge?
acl purgers {
    "localhost";
    "88.151.240.193"; # Change this by the ip address(es) from where you would want to perform PURGE operations
}
 
sub vcl_recv {
 
    if (req.request == "PURGE") {
        if (!client.ip ~ purgers) {
	    error 405 "You are not allowed to purge";
	}
	    return(lookup);
    }
 
    # Set proxied ip header to original remote address
        set req.http.X-Forwarded-For = client.ip;
 
    # If the backend fails, keep serving out of the cache for 30m
        set req.grace = 30m;
 
    # Remove has_js and Google Analytics cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|__utm*|has_js)=[^;]*", "");
 
    # Remove a ";" prefix, if present.
    set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
 
    # Remove empty cookies.
    if (req.http.Cookie ~ "^\s*$") {
            unset req.http.Cookie;
    }
 
    # remove double // in urls,
        set req.url = regsuball( req.url, "//", "/"      );
 
    # Normalize Accept-Encoding
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
            # No point in compressing these
            remove req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unknown algorithm
            remove req.http.Accept-Encoding;
        }
    }
 
    # Remove cookies for static files
    if (req.url ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|mp4|pdf|ico|png|tif|tiff|mp3|htm|html)(\?.*|)$") {
        unset req.http.cookie;
        return(lookup);
    }
 
    # Disable caching for backend parts
    if ( req.url ~ "^/[^?]+/wp-(login|admin)" || req.url ~ "^/wp-(login|admin)" || req.url ~ "preview=true" ) {
        return(pass);
    }
 
    # always pass through posted requests and those with basic auth
    if ( req.request == "POST" || req.http.Authorization ) {
        return (pass);
    }
 
    # Strip cookies for cached content
    unset req.http.Cookie;
    return(lookup);
 
}
 
sub vcl_fetch {
 
    # If the backend fails, keep serving out of the cache for 30m
    set beresp.grace = 30m;
    set beresp.ttl = 48h;
 
    # Remove some unwanted headers
    unset beresp.http.Server;
    unset beresp.http.X-Powered-By;
 
    # Respect the Cache-Control=private header from the backend
    if (beresp.http.Cache-Control ~ "private") {
        set beresp.http.X-Cacheable = "NO:Cache-Control=private";
    } elsif (beresp.ttl < 1s) { 	
        set beresp.ttl   = 5s;
 	set beresp.grace = 5s;
 	set beresp.http.X-Cacheable = "YES:FORCED";
    } else {
 	set beresp.http.X-Cacheable = "YES";
    }
 
    # Don't cache responses to posted requests or requests with basic auth
    if ( req.request == "POST" || req.http.Authorization ) {
         return (hit_for_pass);
    }
 
    # Cache error pages for a short while
    if( beresp.status == 404 || beresp.status == 500 || beresp.status == 301 || beresp.status == 302 ){
         set beresp.ttl = 1m;
         return(deliver);
    }
 
    # Do not cache non-success response
    if( beresp.status != 200 ){
    return(hit_for_pass);
    }
 
    return(deliver); }
 
sub vcl_deliver {
 
    # Add debugging headers to cache requests
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    }
    else {
        set resp.http.X-Cache = "MISS";
    }
 
}
 
sub vcl_error {
 
    # Try connecting to apache 3 times before giving up
    if (obj.status == 503 && req.restarts < 2) {
        set obj.http.X-Restarts = req.restarts;
        return(restart);
    }
    if (obj.status == 301) {
        set obj.http.Location = req.url;
        set obj.status = 301;
        return(deliver);
    }
 
}
 
sub vcl_hit {
 
    if (req.request == "PURGE"){
	set obj.ttl = 0s;
	error 200 "Varnish cache has been purged for this object.";
    }
 
}
 
sub vcl_miss {
 
    if (req.request == "PURGE") {
	error 404 "Object not in cache.";
    }
 
}

Activation

After this configuration I changed Apache and my vhosts to run on port 8080 and Varnish on port 80.

~$ vi /etc/apache2/ports.conf
...
NameVirtualHost *:8080
Listen 8080
...
 
~$ vi /etc/apache2/sites-enabled/dimitrieu
 
...
 
~$ vi /etc/default/varnish
...
DAEMON_OPTS="-a :80 \
             -T localhost:6082 \
             -f /etc/varnish/default.vcl \
             -S /etc/varnish/secret \
             -s malloc,256m"
...
 
~$ /etc/init.d/apache2 restart
~$ /etc/init.d/varnish restart

OpenVZ tweak for Varnish

On a side note; in order to prevent Varnish from eating all of your memory on an OpenVZ platform like Parallels Virtuozzo (where my server is hosted on) you should modify your Varnish init script. Just add the following code somewhere in your init script (/etc/init.d/varnish):

# Make sure OpenVZ does not use up all memory
ulimit -s 2048

If you just want to download my Varnish configuration file you can just click on this link: Varnish vcl for WordPress

Tagged on: , ,

2 thoughts on “Varnish config for WordPress

  1. Pingback: How to purge the Varnish cache | Dimitri.eu

Leave a Reply

Your email address will not be published. Required fields are marked *