Configure WordPress Varnish for Cache with Apache or nginx

wordpress-varnish-cache-configuration-diagram

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.

 

SOURCE: htpcguides

Comments

comments

Leave a Reply