Slapping some Varnish on WordPress

So last week I published my review of PHPNW12 and instantly my whole server went down in flames. My current WP setup has a few issues with multi-lingual plugins which I can’t get over yet, as it requires a bit more of invested time. But this can’t go on, I needed a solution, a fast and simple one to give my blog a fighting chance.

As it stood I already had caching, but i’m guessing that was not enough or decently handled, so I gave a shout out to @thijsferyn to see if putting up Varnish would be a simple enough task. This is BTW one of the reasons I dedicate so much time to helping and growing the community, because when I tread into new waters like this, I can call on smart people who are always willing to lend me a hand. It was no different with Thijs and by the next day I had a recommended setup to try and get Varnish up.

Varnish + WP + ServerGrove

My server is a 1Gb VPS with Servergrove and it runs 3 blogs and a personal website, all smallish with the occasional peak access, so I went on to get things up. Thijs had recommended running Apache on 8080 and leaving Varnish on port 80, but Servergrove is still working on alternative port configuration in their dashboard and they quickly got back to me with the suggestion of putting Apache to listen to localhost on 80 and leaving varnish to handle outside requests. This change was pretty simple I just had to tell Apache to listen to that and change my vhosts:

NameVirtualHost 127.0.0.1:80
Listen 127.0.0.1:80

Problem solved. Now to get varnish. Thijs pointed me to some easy to follow tutorials for each common OS: Ubuntu, Debian, Redhat or CentOS.

Once that was done, I needed to do was configure the basic setup, so I basically had to edit /etc/default/varnish to listen to external requests on port 80:

    DAEMON_OPTS="-a <external-ip>:80 \
                 -T localhost:6082 \
                 -f /etc/varnish/default.vcl \
                 -S /etc/varnish/secret \
                 -s malloc,256m"

This is enough to get Varnish listening and do the trick, but now the important part is to get the right VCL configuration so that you don’t run into troubles with your admin pages, cookies being ignored and so forth. If you want more details on the out-of-the-box setup, go see Thijs’ Vanish in Action slides. So next step, the vcl: I had a gist from Thijs to get is sorted, but I had to make a few changes to it to get it just right, but in general its very straight forward, the main highlights are:

  • It ignores admin pages
  • It ignores logged users
  • It allows purges from WP
  • It adds a HIT/MISS header you can track to see if its being effective.
backend default {
 	.host = "127.0.0.1";
 	.port = "80";
}
acl purge {
	"localhost";
	"<external-ip>";
}
sub vcl_recv {

	# Allow purging
	if(req.request == "PURGE"){
		if(!client.ip ~ purge){
			error 405 "Purging not allowed.";
		}
		return(lookup);
	}

	# Always cache the following file types for all users.
	if ( req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$" ) {
		unset req.http.cookie;
	}

	# Don't serve cached pages to logged in users
	if ( req.http.cookie ~ "wordpress_logged_in" || req.url ~ "vaultpress=true" ) {
		return( pass );
	}

	# Drop any cookies sent to WordPress.
	if ( ! ( req.url ~ "wp-(login|admin)" ) ) {
		unset req.http.cookie;
	}

	# Handle compression correctly. Different browsers send different
	# "Accept-Encoding" headers, even though they mostly all support the same
	# compression mechanisms. By consolidating these compression headers into
	# a consistent format, we can reduce the size of the cache and get more hits.
	# @see: http://varnish.projects.linpro.no/wiki/FAQ/Compression
	if ( req.http.Accept-Encoding ) {

		if ( req.http.Accept-Encoding ~ "gzip" ) {
			# If the browser supports it, we'll use gzip.
			set req.http.Accept-Encoding = "gzip";
		}

		else if ( req.http.Accept-Encoding ~ "deflate" ) {
			# Next, try deflate if it is supported.
			set req.http.Accept-Encoding = "deflate";
		}

		else {
			# Unknown algorithm. Remove it and send unencoded.
			unset req.http.Accept-Encoding;
		}

	}

}

sub vcl_fetch {

	# Allow items to be stale if needed.
	set beresp.grace = 2m;

	# Drop any cookies WordPress tries to send back to the client.
	if ( ! req.url ~ "wp-(login|admin)" && ! req.http.cookie ~ "wordpress_logged_in" ) {
		unset beresp.http.set-cookie;
	}

}

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

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

sub vcl_deliver {
        if(obj.hits > 0) {
                set resp.http.X-Varnish-Cache = "HIT ("+obj.hits+")";
        } else {
                set resp.http.X-Varnish-Cache = "MISS";
        }
}

That was pretty much it. It was then a matter of restarting the services, getting rid of WP cookies so it would not ignore me and asking people to hit the blog up, which showed me a nicely growing hit count on the HIT header and a significant lack of stress on the server. As an extra resource, check this post it has some nice information and configuration settings.

Hopefully the result will be that when I release this post, I will not get lots of replies of my blog being down, but rather Varnish will take care of all of you. The PHP community is truly a remarkable one and it never ceases to amaze me how much we are willing to do for each other and lend a helping hand, so thank you Thijs for the leads.

Also, if you are looking for VPS hosting, I can recommend two services for you: Combell employers of Thijs and Servergrove current hosters of this blog.

3 thoughts on “Slapping some Varnish on WordPress

  1. Varnish definitely does the trick, I'm now able to read this awesome post. And I love the solution of running both on port 80. I too have a hosting setup where changing ports was a reason to keep postponing Varnish. But with this tip I think I have no more reason not to implement it.

  2. Nice post. Another alternative we always recommend for those that don't want to get into Varnish is using nginx in front of Apache, acting as a reverse proxy and cache server. It works very well and it is simpler to configure.

    • Why would you need Apache, if you have nginx (and say php-fpm)? I know there are cases for that, but what's yours?

  3. Rafael,

    Great post, just curious what you’re using to actually purge the cache from Varnish? How do you handle purging the front page or the blog post once someone makes a comment?

    • I enabled the W3 Cache plugin and allowed purging from localhost. I still have to check if that will work, but I can always resort to manual curl calls from the server ssh. I'll update you when i get this checked.

    • I would have to agree with Daniel, if you want more traffic just look at Google. If it was me I would have uninstalled the plugin so that the site worked and kept putting time into creating new content.

      I suppose the fix was an adventure though and in this case lead to some great content.

Leave a Reply