Install and Configure Varnish Cache for WordPress
Install and configure Varnish cache for WordPress on Centos 6
Todays post will show you how to install and configure Varnish 4 cache on your VPS (or dedicated) server to use on your WordPress site.
A fairly common issue WordPress Webmasters have is the larger their wordpress websites get, the slower and slower it also gets. If they use a smaller VPS server for their sites they start to hit memory problems, apache load problems, and a range of other issues.
WordPress has a large number of caching plugins available, and believe me I have tried them all at one stage or another with varying degrees of success.
Varnish is a much more generic caching tool which can be made to work with WordPress using my guide below. You can use it alongside all your regular wordpress caching plugins as well if you like so you can effectively test it live, and if it doesnt help you then you can simply unattach it from your site again and continue as you were.
1) Prepare the Varnish 4 Repository
On Centos 6 you need to install the Varnish repository like this –
rpm -Uvh http://repo.varnish-cache.org/redhat/varnish-4.0/el6/noarch/varnish-release/varnish-release-4.0-4.el6.noarch.rpm
On Centos 7 Varnish 4 is available through the EPEL repository which you possibly already have installed. If you dont then do this –
rpm --import https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6
rpm -Uvh https://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
2) Install Varnish cache for WordPress
As always now you have the repo installed getting varnish on there is simple –
yum install varnish
3) Configure Varnish cache for WordPress
Varnish configuration is stored in a file called /etc/varnish/default.vcl
. vcl is effectively a language in itself, but its fairly standard in its formation of conditional statements. If you have a basic html website then Varnish comes with a default vcl file which will probably work and speed up your site massively. However WordPress uses cookies all over the place which stop Varnish from working its magic to its full potential.
I cannot claim writing this varnish config for wordpress, but I can claim I have added and adjusted bits of it to suit my particular server and what runs on it –
vcl 4.0;
# Default backend definition. Set this to point to your content server.
backend default {
.host = "127.0.0.1";
.port = "81";
.connect_timeout = 600s;
.first_byte_timeout = 600s;
.between_bytes_timeout = 600s;
.max_connections = 800;
}
# Only allow purging from specific IPs
acl purge {
"localhost";
"127.0.0.1";
}
# This function is used when a request is send by a HTTP client (Browser)
sub vcl_recv {
#Force specific urls or vhost urls to pass to the backend (not cached)
if (req.http.host == "XXXXXXXXXXXXXXXXXXXXXXXX" || req.http.host == "YYYYYYYYYYYYYYYYYYYYYYYY") {
return(pass);
}
# Normalize the header, remove the port (in case you're testing this on various TCP ports)
set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");
# Remove has_js and CloudFlare/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*", "");
# Allow purging from ACL
if (req.method == "PURGE") {
# If not allowed then a error 405 is returned
if (!client.ip ~ purge) {
return(synth(405, "This IP is not allowed to send PURGE requests."));
}
# If allowed, do a cache_lookup -> vlc_hit() or vlc_miss()
return (purge);
}
# Post requests will not be cached
if (req.http.Authorization || req.method == "POST") {
return (pass);
}
# --- WordPress specific configuration
# Did not cache the admin and login pages
if (req.url ~ "wp-(login|admin)" || req.url ~ "preview=true") {
return (pass);
}
# Remove the "has_js" cookie
set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");
# Remove any Google Analytics based cookies
set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
# Remove the Quant Capital cookies (added by some plugin, all __qca)
set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");
# Remove the wp-settings-1 cookie
set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", "");
# Remove the wp-settings-time-1 cookie
set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", "");
# Remove the wp test cookie
set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", "");
# Are there cookies left with only spaces or that are empty?
if (req.http.cookie ~ "^ *$") {
unset req.http.cookie;
}
# Cache the following files extensions
if (req.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico)") {
unset req.http.cookie;
}
# Normalize Accept-Encoding header and compression
# https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
if (req.http.Accept-Encoding) {
# Do no compress compressed files...
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
unset 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 {
unset req.http.Accept-Encoding;
}
}
# Check the cookies for wordpress-specific items
if (req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_") {
return (pass);
}
if (!req.http.cookie) {
unset req.http.cookie;
}
# --- End of WordPress specific configuration
# Did not cache HTTP authentication and HTTP Cookie
if (req.http.Authorization || req.http.Cookie) {
# Not cacheable by default
return (pass);
}
# Cache all others requests
return (hash);
}
sub vcl_pipe {
return (pipe);
}
sub vcl_pass {
return (fetch);
}
# The data on which the hashing will take place
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
# If the client supports compression, keep that in a different cache
if (req.http.Accept-Encoding) {
hash_data(req.http.Accept-Encoding);
}
return (lookup);
}
# This function is used when a request is sent by our backend (Nginx server)
sub vcl_backend_response {
if (bereq.url ~ "XXXXXXXXXXXXXXXXXXXXXXXX" || bereq.url ~ "YYYYYYYYYYYYYYYYYYYYYYYY") {
return(deliver);
}
# Remove some headers we never want to see
unset beresp.http.Server;
unset beresp.http.X-Powered-By;
# For static content strip all backend cookies
if (bereq.url ~ "\.(css|js|png|gif|jp(e?)g)|swf|ico") {
unset beresp.http.cookie;
}
# Don't store backend
if (bereq.url ~ "wp-(login|admin)" || bereq.url ~ "preview=true") {
set beresp.uncacheable = true;
set beresp.ttl = 30s;
return (deliver);
}
# Only allow cookies to be set if we're in admin area
if (!(bereq.url ~ "(wp-login|wp-admin|preview=true)")) {
unset beresp.http.set-cookie;
}
# don't cache response to posted requests or those with basic auth
if ( bereq.method == "POST" || bereq.http.Authorization ) {
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# don't cache search results
if ( bereq.url ~ "\?s=" ){
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# only cache status ok
if ( beresp.status != 200 ) {
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# A TTL of 2h
set beresp.ttl = 2h;
# Define the default grace period to serve cached content
set beresp.grace = 30s;
return (deliver);
}
# 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-Cache = "HIT";
} else {
set resp.http.x-Cache = "MISS";
}
# Remove some headers: PHP version
unset resp.http.X-Powered-By;
# Remove some headers: Apache version & OS
unset resp.http.Server;
# Remove some heanders: Varnish
unset resp.http.Via;
unset resp.http.X-Varnish;
return (deliver);
}
sub vcl_init {
return (ok);
}
sub vcl_fini {
return (ok);
}
Config Download link – HERE
Some key parts to note in the config above are –
vcl_recv
– This section is used when varnish receives a request from your browser. It is here where we remove unwanted cookies WordPress generates
vcl_deliver
– It is here where varnish decides if it has any cached content for the request and sends it to the browser
vcl_backend_response
– makes decision if it needs to ask the backend server for data (example wordpress admin pages we would not want to cache). We can also determine here if we want to exclude anything else from being cached (example may be that you have multiple websites but only want to keep your main one in varnish cache)
4) REQUIRED Server config for Varnish Cache for WordPress
Dont start Varnish yet, there are a few other things to do….
vi /etc/sysconfig/varnish
In here you can set the size of the cache to whatever is comfortable for your server by editing VARNISH_STORAGE_SIZE=XXXM
Also as you are wanting to put Varnish in front of your web traffic you will want to set VARNISH_LISTEN_PORT=80
IMPORTANT –
You might remember in the default.vcl file I had set the backend server to be on port 81 (near the top of the vcl file). This means you will also need to adjust your apache/nginx backend server vhost(s) to run on that port.
IF you have done all the above correct (and if my notes are right…) then we are almost there. You might want to set Varnish to auto start on server reboot like this –
chkconfig varnish on
After updating your backend vhosts port you will want to restart apache –
service httpd restart
Then you are ready to start Varnish –
service varnish start
Some stats are always nice…. so you got these to have a play with
varnishstat
– updating screen to show counts of your cache hits and misses
varnishlog
– if you really want to dig into some more detail
Want some more Varnish addons for monitoring or additional tools?
http://varnish-cache.org/extras/index.html#extras
Things to note –
– If you are upgrading from Varnish 3 to Varnish 4 then there are some changes you will need to make to your default.vcl file, it is not a straightforward upgrade and expect everything to work
– Check the Varnish website for much more detail about each of the sections in the config for Varnish. As I said above the config I have shown you here was not created by me, but works with WordPress. It can be added to for individual needs and server configurations
If you found this post useful then subscribe to the site for new posts which I do every week or so. Also check out my other TUTORIALS and BASH scripting hints and tips.