Varnish is one of the best WooCommerce caching solutions I have tried. I’ve managed to get WooCommerce shop load times under 1 second! The Varnish control language (vcl) gives you incredible control over what should be cached and how, providing greater flexibility than with nginx proxy or fastcgi cache systems.
Using Varnish will give your web server running nginx, PHP and MariaDB or MySQL some relief so that they will only be used when customers are actually adding products to their shopping cart. Window shoppers will only be using Varnish for browsing providing awesome speed.
Install + Configure Varnish 3 Cache with nginx for WooCommerce Speed
Installation Overview
- Configure nginx running WooCommerce to use Varnish
- Install Varnish 3 Cache
- Configure Varnish Cache for WooCommerce
Configure WooCommerce to Use Varnish 3 Cache
Varnish will listen on port 80 and you will change nginx to listen on port 8080
Configure nginx for Varnish Cache
Open your nginx virtual host file, it may not be called woocommerce
sudo nano /etc/nginx/sites-available/woocommerce
In your server block change the port from 80
to 8080
, you will need to do this for all of your nginx virtual hosts.
server {
server_name www.wp-bullet.com wp-bullet.com;
listen 8080;
Ctrl+X, Y and Enter to save
To get the Real IP of your WooCommerce 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 of your WooCommerce customers from CloudFlare and Varnish, we are using the X-Actual IP in the default.vcl created later on.
#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.
Test the nginx virtual host configuration is valid.
sudo nginx -t
We will restart nginx later.
Install Varnish 3 Cache
Add the Varnish repository for Debian, you may need to adjust wheezy to jessie if you have upgraded to Debian Jessie.
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/ wheezy varnish-3.0" | sudo tee -a /etc/apt/sources.list.d/varnish-cache.list
echo "deb-src https://repo.varnish-cache.org/debian/ wheezy varnish-3.0" | sudo tee -a /etc/apt/sources.list.d/varnish-cache.list
sudo apt-get update
sudo apt-get install varnish -y
If you are on Ubuntu 14.04 LTS then use these steps
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/ubuntu/ trusty varnish-3.0" | sudo tee -a /etc/apt/sources.list.d/varnish-cache.list
echo "deb-src https://repo.varnish-cache.org/debian/ trusty varnish-3.0" | sudo tee -a /etc/apt/sources.list.d/varnish-cache.list
sudo apt-get update
sudo apt-get install varnish -y
Enable Varnish 3 Cache to listen on port 80 and start the daemon with the init.d startup script with these steps.
If you are on Debian Jessie you will need to update the Varnish systemd script outlined here, return to this page for the Varnish 3 vcl.
sudo nano /etc/default/varnish
Change this line to enable the Varnish daemon
START=yes
Then scroll down to Alternative 2 and change 6081 to 80 so Varnish will listen on port 80
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-s malloc,256m"
Configure Varnish 3 Cache for WooCommerce
Now back up the Varnish vcl file which contains all of the rules for treating requests and URLs
sudo mv /etc/varnish/default.vcl /etc/varnish/default.vcl.bak
Create the new Varnish vcl rules configuration file
sudo nano /etc/varnish/default.vcl
This Varnish 3 vcl was adapted from here and here to be optimized for WooCommerce.
Paste this WooCommerce Varnish vcl configuration, replace Web.Server.IP
with your web server’s IP
/* SET THE HOST AND PORT OF WooCommerce
* *********************************************************/
backend default {
.host = "127.0.0.1";
.port = "8080";
}
# SET THE ALLOWED IP OF PURGE REQUESTS
# ##########################################################
acl purge {
"localhost";
"127.0.0.1";
"Web.Server.IP";
}
#THE RECV FUNCTION
# ##########################################################
sub vcl_recv {
#remove HTTPOXY CGI vulnerability
unset req.http.proxy;
#remove extraneous host ports
set req.http.host = regsub(req.http.Host, ":[0-9]+", "");
# 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, "[, ].*$", "");
# Enable smart refreshing
if (req.http.Cache-Control ~ "no-cache" && client.ip ~ purge) {
set req.hash_always_miss = true;
}
# 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 );
# 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;
}
}
# DO NOT CACHE RSS FEED
if (req.url ~ "/feed/") {
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
remove 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
remove req.http.Accept-Encoding;
}
}
# IF THIS IS A PURGE REQUEST, THEN CHECK THE IPS SET ABOVE
# BLOCK IF NOT ONE OF THOSE IPS
# ##########################################################
if (req.request == "PURGE") {
if ( !client.ip ~ purge ) {
error 405 "Not allowed.";
}
return (lookup);
}
# PIPE ALL NON-STANDARD REQUESTS
# ##########################################################
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
return (pipe);
}
# ONLY CACHE GET AND HEAD REQUESTS
# ##########################################################
if (req.request != "GET" && req.request != "HEAD") {
return (pass);
}
# OPTIONAL: DO NOT CACHE LOGGED IN USERS and CARTS
# ##########################################################
if ( req.http.cookie ~ "wordpress_logged_in|resetpass" ) {
return( pass );
}
if (req.url ~ "/wp-(login|admin|cron)|wc-api|cart|my-account|checkout|addons|administrator|resetpass|\?wc-ajax=get_refreshed_fragments") {
# Don't cache, pass to backend
return (pass);
}
if ( req.url ~ "\?add-to-cart=" ) {
return (pass);
}
#fixed non AJAX cart problem, may need to add wp_woocommerce_session_
if (req.http.cookie ~ "woocommerce_(cart|session)") {
return(lookup);
}
if (!req.url ~ "/wp-(login|admin|cron)|wc-api|cart|my-account|checkout|addons|administrator|resetpass") {
# Don't cache, pass to backend
unset req.http.cookie;
}
# This is for phpmyadmin
if (req.http.Host == "pmadomain.com") {
return (pass);
}
# IF YOU GET HERE THEN THIS REQUEST SHOULD BE CACHED
# ##########################################################
return (lookup);
}
sub vcl_hash {
#this is to store cache based on PHPSESSID or woocommerce cookie so cart doesn't show 0
if (req.http.cookie) {
hash_data(req.http.cookie);
}
#fix flexible ssl css
if (req.http.x-forwarded-proto) {
hash_data(req.http.x-forwarded-proto);
}
}
# HIT FUNCTION
# ##########################################################
sub vcl_hit {
# IF THIS IS A PURGE REQUEST THEN DO THE PURGE
# ##########################################################
if (req.request == "PURGE") {
purge;
error 200 "Purged.";
}
return (deliver);
}
# MISS FUNCTION
# ##########################################################
sub vcl_miss {
if (req.request == "PURGE") {
purge;
error 200 "Purged.";
}
return (fetch);
}
# FETCH FUNCTION
# ##########################################################
sub vcl_fetch {
# 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";
# You may need to add other locations like membership sites here, 302 is necessary if you use redirect to cart
# ##########################################################
if (!(req.url ~ "wp-(login|admin)|wc-api|resetpass|cart|checkout|my-account|\?wc-ajax=get_refreshed_fragments") &&
!req.http.cookie ~ "wordpress_logged_in|resetpass" &&
!beresp.status == 302) {
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;
return (hit_for_pass);
}
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
Test the Varnish vcl syntax is valid
sudo varnishd -C -f /etc/varnish/default.vcl
If you got no errors then you can restart nginx and Varnish
sudo service nginx restart && sudo service varnish restart
Enjoy your super fast WooCommerce site powered by Varnish cache.
Hello, great post. Do you have figured out a .vcl for Varnish 4 and woocommerce?
I do have a base configuration which I tailor for clients. I hope to post it eventually but will probably wait until the newer Woo improvements drop.
Hello Mike,
thank you for your reply. Is it possible to send me the config via email?
I am currently not sending it out except to clients on codeable, sorry Andrej!
How can I hire you for a site with a 500,000+ sessions per month, 60+ per second active
Hi Anthony, I work through https://guides.wp-bullet.com/codeable-hire/ exclusively so please get in touch there. I am on vacation until July 10th just so you know.
Hi, all the product pages on my website cache fine with this, but am unable to add any items to the cart any ideas.