WordPress Varnish caching

Varnish is a very common caching tool many wordpress blogs (but certainly not limited to just wordpress) utilize to speed up content delivery.

Here’s a very basic example on how to set it up:

vcl 4.0;


# After VCL loaded, before any requests pass through it.
# Could be used to init VMODs.
sub vcl_init {

}


# Allowed ips/hosts to run purges
acl purge {
  "localhost";
  "127.0.0.1";
  "::1";
  "bytepursuits-wp";
}

# Called at the beginning of a request, after request has been received and parsed.
# Purpose is to decide whether or not to serve the request. Also used to modify the request
sub vcl_recv {
    
 # Remove any Google Analytics based cookies
  set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "_gat=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", "");

  # Remove Optimizely Cookies
  set req.http.Cookie = regsuball(req.http.Cookie, "optim.=[^;]+(; )?", "");

  # Remove a ";" prefix in the cookie if present
  set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

  # Allow purging
  if (req.method == "PURGE") {
    if (!client.ip ~ purge) { # purge is the ACL defined at the begining
      # Not from an allowed IP? Then die with an error.
      return (synth(405, "This IP is not allowed to send PURGE requests."));
    }
    # If you got this stage (and didn't error out above), purge the cached result
    return (purge);
  }

  if (req.method != "GET" &&
      req.method != "HEAD" &&
      req.method != "PUT" &&
      req.method != "POST" &&
      req.method != "TRACE" &&
      req.method != "OPTIONS" &&
      req.method != "DELETE") {
        # Non-RFC2616 or CONNECT which is unexpected.
        return (pipe);
  }

  if (req.method != "GET" && req.method != "HEAD") {
    # We only deal with GET and HEAD by default
    return (pass);
  }

  if(req.url ~ "^/cron.php") {
    return(pass);
  }
  if(req.url ~ "^/xmlrpc.php") {
    return(pass);
  }
  if (req.http.Authorization) {
    return(pass);
  }
  if(req.http.cookie ~ "(^|;\s*)(SESS=)") {
    return(pass);
  }

  if (req.url ~ "wp-admin|wp-login") {
      return (pass);
  }

  return (hash);
}


# The routine when we deliver the HTTP request to the user
# Last chance to modify headers that are sent to the client
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-vcache = "HIT";
  } else {
    set resp.http.X-vcache = "MISS";
  }
}



backend default {
    .host = "bytepursuits-wp";
    .port = "80";
}

Where bytepursuits-wp in above example is the hostname of my wordpress server. In example above I use docker-compose and as we know – with docker container name becomes a hostname, hence the lack of TLD part.

In fact this site does use varnish, so you can check if page was served via varnish or not by opening the developer tools and looking at the response headers.

x-vcache = HIT would then signify that page was served via varnish cache.

Leave a Comment