WordPress and Varnish

Posted by on Jul 14, 2013 in Varnish, Wordpress | 4 Comments

After a couple of outages I decided to move my website to a new host.  I found Digital Ocean which offers VPS servers for a very reasonable price.  This would now allow me to totally manage everything about my hosting including adding Varnish which is a web application accelerator.  One of the things with Varnish is that it doesn’t work well with cookies which WordPress uses alot, so if your site it very dynamic then this probably isn’t the solution for you but as my site doesn’t have any dynamic content this is a ideal solution to speed up my site.

It may sound like a lot of work to configure your own server but there are some very good documents on the Digital Ocean site which shows you exactly how to do a lot of the common tasks such as installing and configuring everything you need for a full varnish powered WordPress site.

If you are going to use Varnish for your WordPress site here is a good configuration that will give you a very good start out of the box.

If you just want to have a play about then try Digital Ocean as you can create a server play around and then trash it and it will only cost you $0.007 a hour.


# This is a basic VCL configuration file for varnish. See the vcl(7)
 # man page for details on VCL syntax and semantics.
 #
 # Default backend definition. Set this to point to your content
 # server.
 #
 backend default {
 .host = "127.0.0.1";
 .port = "8080";
 }

acl purge {
 # For now, I'll only allow purges coming from localhost
 "127.0.0.1";
 "localhost";
 }
 # Drop any cookies WordPress tries to send back to the client.
 sub vcl_fetch {

if (req.url ~ "\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$") {
 unset beresp.http.set-cookie;
 set beresp.ttl = 24h;
 }

# Varnish determined the object was not cacheable
 if (!beresp.ttl > 0s) {
 set beresp.http.X-Cacheable = "NO:Not Cacheable";
 } else if ( req.http.Cookie ~"(wp-postpass|wordpress_logged_in|comment_author_)" ) {
 # You don't wish to cache content for logged in users
 set beresp.http.X-Cacheable = "NO:Got Session";
 return(hit_for_pass);
 } else if ( beresp.http.Cache-Control ~ "private") {
 # You are respecting the Cache-Control=private header from the backend
 set beresp.http.X-Cacheable = "NO:Cache-Control=private";
 return(hit_for_pass);
 } else if ( beresp.ttl < 1s ) {
 # You are extending the lifetime of the object artificially
 set beresp.ttl = 300s;
 set beresp.grace = 300s;
 set beresp.http.X-Cacheable = "YES:Forced";
 } else {
 # Varnish determined the object was cacheable
 set beresp.http.X-Cacheable = "YES";
 }
 if (beresp.status == 404 || beresp.status >= 500) {
 set beresp.ttl = 0s;
 }

# Deliver the content
 return(deliver);
 }

sub vcl_hash {
 # Each cached page has to be identified by a key that unlocks it.
 # Add the browser cookie only if a WordPress cookie found.
 if ( req.http.Cookie ~"(wp-postpass|wordpress_logged_in|comment_author_)" ) {
 hash_data(req.http.Cookie);
 }
 }

# Deliver
 sub vcl_deliver {
 # Uncomment these lines to remove these headers once you've finished setting up Varnish.
 remove resp.http.X-Varnish;
 remove resp.http.Via;
 remove resp.http.Age;
 remove resp.http.X-Powered-By;
 }

# vcl_recv is called whenever a request is received
 sub vcl_recv {

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

# remove ?ver=xxxxx strings from urls so css and js files are cached.
 # Watch out when upgrading WordPress, need to restart Varnish or flush cache.
 set req.url = regsub(req.url, "\?ver=.*$", "");

# Remove "replytocom" from requests to make caching better.
 set req.url = regsub(req.url, "\?replytocom=.*$", "");

remove req.http.X-Forwarded-For;
 set req.http.X-Forwarded-For = client.ip;

# Exclude this site because it breaks if cached
 #if ( req.http.host == "example.com" ) {
 # return( pass );
 #}

# Serve objects up to 2 minutes past their expiry if the backend is slow to respond.
 set req.grace = 120s;
 # Strip cookies for static files:
 if (req.url ~ "\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$") {
 unset req.http.Cookie;
 return(lookup);
 }
 # Remove has_js and Google Analytics __* cookies.
 set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|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;
 }

# Allow purging
 if (req.request == "PURGE") {
 if (!client.ip ~ purge) {
 # Not from an allowed IP? Then die with an error.
 error 405 "This IP is not allowed to send PURGE requests.";
 }

# If you got this stage (and didn't error out above), do a cache-lookup
 # That will force entry into vcl_hit() or vcl_miss() below and purge the actual cache
                ban("req.http.host == " +req.http.host+" && req.url ~ "+req.url);
                error 200 "Ban added";
 }

# Pass anything other than GET and HEAD directly.
 if (req.request != "GET" && req.request != "HEAD") {
 return( pass );
 } /* We only deal with GET and HEAD by default */

# remove cookies for comments cookie to make caching better.
 set req.http.cookie = regsub(req.http.cookie, "1231111111111111122222222333333=[^;]+(; )?", "");

# never cache the admin pages, or the server-status page
 if (req.request == "GET" && (req.url ~ "(wp-admin|bb-admin|server-status)")) {
 return(pipe);
 }
 # don't cache authenticated sessions
 if (req.http.Cookie && req.http.Cookie ~ "(wordpress_|PHPSESSID)") {
 return(pass);
 }
 # don't cache ajax requests
 if(req.http.X-Requested-With == "XMLHttpRequest" || req.url ~ "nocache" || req.url ~ "(control.php|wp-comments-post.php|wp-login.php|bb-login.php|bb-reset-password.php|register.php)") {
 return (pass);
 }
 return( lookup );
 }

4 Comments

  1. Farzana Neha @ Bangla Movie
    July 26, 2013

    Do you know how can I purge varnish cache from command line? Or, is there any plugin to do so? Sometimes it’s handy when we change something on the site (not page/posts).

    Reply
    • james
      July 26, 2013

      Yes there are a couple of ways that you can purge pages/elements from the command line, as long as your VCL is setup to accept the purge requests you can do it via curl, e.g. curl -X PURGE http://www.binaryforest.com/wordpress-and-varnish/

      You can also use varnishadm and the ban.url option in there.

      There are a couple of plugins which do some of this as well such as “Better WP Varnish”

      Reply
      • Farzana Neha @ Bangla Movie
        July 26, 2013

        Thanks for your response, James. Yes, “Better WP Varnish” seems working. But, my bad … my DNS didn’t resolve properly and I was trying to purge cache using the plugin on my other host which didn’t have varnish.

        However, I was trying varnishadm a few hours ago. It’s giving me authentication error. I will try with curl.

        Thanks again for your time 🙂

        Reply
        • james
          July 26, 2013

          If you are using varnishadm you need to have root privileges to run as it needs to read the varnish secret file which is only readable by root.

          Reply

Leave a Reply