Varnish is a refresh proxy that serves your WordPress site super fast. It will cache your WordPress site as compiled html pages so users avoid the web server (Apache2 and nginx) and slow PHP. It is very easy to set up WordPress Varnish cache on your VPS or dedicated server.
This guide is for Debian Jessie but Wheezy will work, so too will Ubuntu and Raspberry Pi, Banana Pi devices. You must be on a 64-bit machine to use Varnish 4's repository, however Debian 8 Jessie 32-bit has Varnish 4 in its repository. The only limitation I have experienced so far using Varnish is using a custom renamed login page with Apache 2 which can now be fixed by adding exceptions i backend_fetch. but I recommend protecting your login page with a free CloudFlare account.
Varnish gives you the fastest load times that a regular WordPress PHP backend alone has difficulty competing with a VPS like Digital Ocean ($5 a month). Bear in mind this site would take over 3 seconds to load because I have a lot of plugins (40+).
VPS Provider | |||||
---|---|---|---|---|---|
Vultr | |||||
Digital Ocean | |||||
HostUS |
If you want to install Varnish 5 see this guide.
I suggest you take speed tests using pingdom now and compare them to your results post Varnish configuration.
Configure WordPress Varnish 4 Cache with Apache or nginx
The diagram on the left shows the traditonal server setup. nginx or Apache listens on port 80. On the right is the new setup, Varnish will listen on port 80 and you will change your web server to listen on port 8080.
Configure Web Server to Use Varnish
Configure Apache for Varnish Cache
Open your Apache configuration file
sudo nano /etc/apache2/ports.conf
Change these lines so Apache listens on port 8080 instead
NameVirtualHost *:8080
Listen 8080
Ctrl+X, Y and Enter to save
Now adjust your Apache virtual host files for WordPress
sudo nano /etc/apache2/sites-available/wordpress
Change this line to match so the virtual host listens on port 8080
<VirtualHost 127.0.0.1:8080>
Ctrl+X, Y and Enter to save. Do not restart Apache yet
Configure nginx for Varnish Cache
Make sure you have disabled the default virtual host
sudo unlink /etc/nginx/sites-enabled/default
Open your nginx virtual host file, it may not be called wordpress
sudo nano /etc/nginx/sites-available/wordpress
In your server block change the port from 80 to 8080
server {
server_name www.htpcguides.com htpcguides.com;
listen 8080;
Ctrl+X, Y and Enter to save
To get the Real IP of your WordPress user comments and emails from Varnish, use the Real IP feature of nginx
sudo nano /etc/nginx/conf.d/cloudflare.conf
Paste this to get the Real IP from CloudFlare and Varnish, we are using the X-Actual IP created earlier
#CloudFlare
set_real_ip_from 199.27.128.0/21;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
#For use with Varnish
set_real_ip_from 127.0.0.1/32;
real_ip_header X-Actual-IP;
Ctrl+X, Y and Enter to save the WordPress nginx real IP configuration
We will restart nginx later
Install Varnish 4 Cache
Add the Varnish 4 Cache repository, you may need to adjust jessie to wheezy if you are not on Jessie yet. If using Ubuntu you can change jessie to precise (12.04) or trusty (14.04 and later).
Remember you need to be on a 64-bit distro to get the Varnish 4 binary from the repository, 32-bit users should use the next instructions.
sudo apt-get install apt-transport-https -y
wget -O - https://repo.varnish-cache.org/GPG-key.txt | sudo apt-key add -
echo "deb https://repo.varnish-cache.org/debian/ jessie varnish-4.1" >> /etc/apt/sources.list.d/varnish-cache.list
echo "deb-src https://repo.varnish-cache.org/debian/ jessie varnish-4.1" >> /etc/apt/sources.list.d/varnish-cache.list
sudo apt-get update
sudo apt-get install varnish -y
Debian 8 Jessie has Varnish 4.0.2 in its repository
sudo apt-get update
sudo apt-get install varnish -y
If you are using systemd the file /lib/systemd/system/varnish.service
will exist, change the Varnish systemd service to use port 80
sudo mkdir -p /etc/systemd/system/varnish.service.d
sudo nano /etc/systemd/system/varnish. service.d/local.conf
Paste these lines (thanks to the Arch page)
[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m
Ctrl+X, Y and Enter to save
If you do not have the Varnish systemd script then you are probably using an init.d script in which case change 6081 to 80 in this file
sudo nano /etc/default/varnish
Back up the default Varnish 4 vcl file
mv /etc/varnish/default.vcl /etc/varnish/default.vcl.bak
Create your new WordPress Varnish 4 cache vcl file
sudo nano /etc/varnish/default.vcl
Paste the vcl adapted from here, replace the Web.Server.IP with the IP of your server (can get your IP with ifconfig
). The purge acl is used to verify curl requests to refresh the cache and smart refresh the Varnish cache using hash_always_miss
from your web server are the only IPs that can refresh or purge the Varnish cache. This vcl is designed for use with the smart refresh scripts and behind CloudFlare.
/* SET THE HOST AND PORT OF WORDPRESS * *********************************************************/ vcl 4.0; import std; backend default { .host = "127.0.0.1"; .port = "8080"; .first_byte_timeout = 60s; .connect_timeout = 300s; } # SET THE ALLOWED IP OF PURGE REQUESTS # ########################################################## acl purge { "localhost"; "127.0.0.1"; "Web.Server.IP"; } #THE RECV FUNCTION # ########################################################## sub vcl_recv { # set realIP by trimming CloudFlare IP which will be used for various checks set req.http.X-Actual-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", ""); # FORWARD THE IP OF THE REQUEST if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } } # Purge request check sections for hash_always_miss, purge and ban # BLOCK IF NOT IP is not in purge acl # ########################################################## # Enable smart refreshing using hash_always_miss if (req.http.Cache-Control ~ "no-cache") { if (client.ip ~ purge || std.ip(req.http.X-Actual-IP, "1.2.3.4") ~ purge) { set req.hash_always_miss = true; } } if (req.method == "PURGE") { if (!client.ip ~ purge || !std.ip(req.http.X-Actual-IP, "1.2.3.4") ~ purge) { return(synth(405,"Not allowed.")); } return (purge); } if (req.method == "BAN") { # Same ACL check as above: if (!client.ip ~ purge || !std.ip(req.http.X-Actual-IP, "1.2.3.4") ~ purge) { return(synth(403, "Not allowed.")); } ban("req.http.host == " + req.http.host + " && req.url == " + req.url); # Throw a synthetic page so the # request won't go to the backend. return(synth(200, "Ban added")); } # Unset cloudflare cookies # 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*", ""); # For Testing: If you want to test with Varnish passing (not caching) uncomment # return( pass ); # DO NOT CACHE RSS FEED if (req.url ~ "/feed(/)?") { return ( pass ); } #Pass wp-cron if (req.url ~ "wp-cron\.php.*") { return ( pass ); } ## Do not cache search results, comment these 3 lines if you do want to cache them if (req.url ~ "/\?s\=") { return ( pass ); } # CLEAN UP THE ENCODING HEADER. # SET TO GZIP, DEFLATE, OR REMOVE ENTIRELY. WITH VARY ACCEPT-ENCODING # VARNISH WILL CREATE SEPARATE CACHES FOR EACH # DO NOT ACCEPT-ENCODING IMAGES, ZIPPED FILES, AUDIO, ETC. # ########################################################## if (req.http.Accept-Encoding) { if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") { # No point in compressing these 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 { # unknown algorithm unset req.http.Accept-Encoding; } } # PIPE ALL NON-STANDARD REQUESTS # ########################################################## if (req.method != "GET" && req.method != "HEAD" && req.method != "PUT" && req.method != "POST" && req.method != "TRACE" && req.method != "OPTIONS" && req.method != "DELETE") { return (pipe); } # ONLY CACHE GET AND HEAD REQUESTS # ########################################################## if (req.method != "GET" && req.method != "HEAD") { return (pass); } # OPTIONAL: DO NOT CACHE LOGGED IN USERS (THIS OCCURS IN FETCH TOO, EITHER # COMMENT OR UNCOMMENT BOTH # ########################################################## if ( req.http.cookie ~ "wordpress_logged_in|resetpass" ) { return( pass ); } #fix CloudFlare Mixed Content with Flexible SSL if (req.http.X-Forwarded-Proto) { return(hash); } # IF THE REQUEST IS NOT FOR A PREVIEW, WP-ADMIN OR WP-LOGIN # THEN UNSET THE COOKIES # ########################################################## if (!(req.url ~ "wp-(login|admin)") && !(req.url ~ "&preview=true" ) ){ unset req.http.cookie; } # IF BASIC AUTH IS ON THEN DO NOT CACHE # ########################################################## if (req.http.Authorization || req.http.Cookie) { return (pass); } # IF YOU GET HERE THEN THIS REQUEST SHOULD BE CACHED # ########################################################## return (hash); # This is for phpmyadmin if (req.http.Host == "pmadomain.com") { return (pass); } } sub vcl_hash { if (req.http.X-Forwarded-Proto) { hash_data(req.http.X-Forwarded-Proto); } } # HIT FUNCTION # ########################################################## sub vcl_hit { return (deliver); } # MISS FUNCTION # ########################################################## sub vcl_miss { return (fetch); } # FETCH FUNCTION # ########################################################## sub vcl_backend_response { # I SET THE VARY TO ACCEPT-ENCODING, THIS OVERRIDES W3TC # TENDANCY TO SET VARY USER-AGENT. YOU MAY OR MAY NOT WANT # TO DO THIS # ########################################################## set beresp.http.Vary = "Accept-Encoding"; # IF NOT WP-ADMIN THEN UNSET COOKIES AND SET THE AMOUNT OF # TIME THIS PAGE WILL STAY CACHED (TTL), add other locations or subdomains you do not want to cache here in case they set cookies # ########################################################## if (!(bereq.url ~ "wp-(login|admin)") && !bereq.http.cookie ~ "wordpress_logged_in|resetpass" ) { unset beresp.http.set-cookie; set beresp.ttl = 1w; set beresp.grace =3d; }
if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") { set beresp.ttl = 120 s; # set beresp.ttl = 120s; set beresp.uncacheable = true; return (deliver); } return (deliver); } # DELIVER FUNCTION # ########################################################## sub vcl_deliver { # IF THIS PAGE IS ALREADY CACHED THEN RETURN A 'HIT' TEXT # IN THE HEADER (GREAT FOR DEBUGGING) # ########################################################## if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; # IF THIS IS A MISS RETURN THAT IN THE HEADER # ########################################################## } else { set resp.http.X-Cache = "MISS"; } }
Ctrl+X, Y and Enter to Save
Reload, enable and restart the Varnish systemd service, restart the nginx service before Varnish
sudo systemctl daemon-reload
sudo systemctl enable varnish
sudo service nginx restart
sudo service varnish restart
Reload, enable and restart the Varnish systemd service, restart the Apache service before Varnish
sudo systemctl daemon-reload
sudo systemctl enable varnish
sudo service apache2 restart
sudo service varnish restart
Lastly, to fix any compatibility issues with WordPress Varnish plugins add your hostname to your VPS hosts file
sudo nano /etc/hosts
Optionally, add your host to the loopback address 127.0.0.1 and also for your VPS public IP.
Warning this could cause issues with postfix sending email.
127.0.0.1 localhost.localdomain localhost htpcguides.com www.htpcguides.com
Ctrl+X, Y and Enter to Save
You can test Varnish by going to Is Varnish Working?
Consider setting up Varnish DDoS protection and the Varnish Web Application Firewall to prevent WordPress SQL injection and XSS attacks is in development.