Skip to content

Instantly share code, notes, and snippets.

@slivorezka
Created August 17, 2018 08:16
Show Gist options
  • Save slivorezka/438db1c15a49463e7b6d1d1316caa581 to your computer and use it in GitHub Desktop.
Save slivorezka/438db1c15a49463e7b6d1d1316caa581 to your computer and use it in GitHub Desktop.
Drupal 8 Varnish
#
# This is an example VCL file for Varnish.
#
# It does not do anything by default, delegating control to the
# builtin VCL. The builtin VCL is called when there is no explicit
# return statement.
#
# See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/
# and https://www.varnish-cache.org/trac/wiki/VCLExamples for more examples.
# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;
# Default backend definition. Set this to point to your content server.
backend default {
.host = "127.0.0.1";
.port = "8080";
.max_connections = 250;
.connect_timeout = 300s;
.first_byte_timeout = 300s;
.between_bytes_timeout = 300s;
}
acl purge {
"127.0.0.1";
}
acl internal {
"127.0.0.1";
# For remote access, add your IP address here.
# Ex: 162.xxx.xx.xx
}
sub vcl_recv {
# Happens before we check if we have this in cache already.
#
# Typically you clean up the request here, removing cookies you don't need,
# rewriting the request, etc.
# Return (pass) instructs Varnish not to cache the request
# when the condition is met.
# Force look-up if request is a no-cache request.
if (req.http.Cache-Control ~ "no-cache") {
return (pass);
}
# Disallow outside access to cron.php or install.php
if (req.url ~ "^/(cron|install|update)\.php$" && !client.ip ~ internal) {
# Either let Varnish throw the error directly error 404 "Page not found.";
# Or, use a custom error page that you've defined in Drupal at the path "404".
# set req.url = "/404";
return (synth(404, "Not Found."));
}
# Add an X-Forwarded-For header with the client IP address.
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;
}
}
## Method ##
if (req.method == "URIBAN") {
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."));
}
# Check the incoming request type is "PURGE", not "GET" or "POST".
if (req.method == "PURGE") {
# Check if the IP is allowed.
if (!client.ip ~ purge) {
# Return error code 405 (Forbidden) when not.
return (synth(405, "Not allowed."));
}
return (purge);
}
# Only allow BAN requests from IP addresses in the 'purge' ACL.
if (req.method == "BAN") {
# Same ACL check as above:
if (!client.ip ~ purge) {
return (synth(403, "Not allowed."));
}
# Clean cache by Cache-Tags heder.
if (req.http.Cache-Tags) {
ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
}
else {
return (synth(403, "Cache-Tags header missing."));
}
# Logic for banning everything
return (synth(200, "Ban added."));
}
# Only cache GET and HEAD requests (pass through POST requests).
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
## ADMIN PAGES ##
# Here we filter out all URLs containing Drupal administrative sections
if (req.url ~ "^/status\.php$" || req.url ~ "^/update\.php$" || req.url ~ "^/admin$" || req.url ~ "^/admin/.*$" || req.url ~ "^/user$" || req.url ~ "^/user/.*$" || req.url ~ "^/flag/.*$" || req.url ~ "^.*/ajax/.*$" || req.url ~ "^/system/files/.*$") {
return (pass);
}
## BACKUP AND MIGRATE MODULE ##
# Backup and Migrate is a very popular Drupal module that needs to be excluded
# It won't work with Varnish
if (req.url ~ "^/admin/content/backup_migrate/export") {
return (pipe);
}
## COOKIES ##
# Remove cookies for stylesheets, scripts, and images used throughout the site.
# Removing cookies will allow Varnish to cache those files.
if (req.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|csv|png|svg|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
# Stop processing further for those file types. This way, we ensure to have
# X-Varnish-Cache HIT for those requests.
unset req.http.Cookie;
}
# Remove all cookies that are not necessary for Drupal to work properly.
# Since it would be cumbersome to REMOVE certain cookies, we specify
# which ones are of interest to us, and remove all others. In this particular
# case we leave SESS, SSESS and NO_CACHE cookies used by Drupal's administrative
# interface. Cookies in cookie header are delimited with ";", so when there are
# many cookies, the header looks like "Cookie1=value1; Cookie2=value2; Cookie3..."
# and so on. That allows us to work with ";" to split cookies into individual
# ones.
#
# The method for filtering unnecessary cookies has been adopted from:
# https://fourkitchens.atlassian.net/wiki/display/TECH/Configure+Varnish+3+for+Drupal+7
if (req.http.Cookie) {
# 1. We add ; to the beginning of cookie header
set req.http.Cookie = ";" + req.http.Cookie;
# 2. We remove spaces following each occurence of ";". After this operation
# all cookies are delimited with no spaces.
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
# 3. We replace ";" INTO "; " (adding the space we have previously removed) in cookies
# named SESS..., SSESS... and NO_CACHE. After this operation those cookies will be
# easy to differentiate from the others, because those will be the only one with space
# after ";"
set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE)=", "; \1=");
# 4. We remove all cookies with no space after ";", so basically we remove all cookies other
# than those above.
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
# 5. We strip leading and trailing whitespace and semicolons.
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
# If there are no cookies after our striping procedure, we remove the header altogether,
# thus allowing Varnish to cache this page
if (req.http.Cookie == "") {
unset req.http.Cookie;
}
# if any of our cookies of interest are still there, we disable caching and pass the request
# straight to Apache and Drupal
else {
return (pass);
}
}
}
sub vcl_backend_response {
# Happens after we have read the response headers from the backend.
#
# Here you clean the response headers, removing silly Set-Cookie headers
# and other mistakes your backend does.
# Set ban-lurker friendly custom headers.
set beresp.http.X-Url = bereq.url;
set beresp.http.X-Host = bereq.http.host;
# Cache 404s, 301s, at 500s with a short lifetime to protect the backend.
if (beresp.status == 404 || beresp.status == 301 || beresp.status == 500) {
set beresp.ttl = 10m;
}
# Remove cookies for stylesheets, scripts and images used throughout the site.
# Removing cookies will allow Varnish to cache those files. It is uncommon for
# static files to contain cookies, but it is possible for files generated
# dynamically by Drupal. Those cookies are unnecessary, but could prevent files
# from being cached.
if (bereq.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|csv|png|svg|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
unset beresp.http.set-cookie;
}
# Allow items to remain in cache up to 6 hours past their cache expiration.
set beresp.grace = 6h;
}
sub vcl_deliver {
# Happens when we have all the pieces we need, and are about to send the
# response to the client.
#
# You can do accounting or modifying the final object here.
# Remove ban-lurker friendly custom headers when delivering to client.
unset resp.http.X-Url;
unset resp.http.X-Host;
unset resp.http.Cache-Tags;
# Reject use cache on the client.
if (resp.http.Content-Type ~ "text/html") {
set resp.http.Cache-Control = "private, no-cache";
}
if (obj.hits > 0) {
set resp.http.X-Varnish-Cache = "HIT";
}
else {
set resp.http.X-Varnish-Cache = "MISS";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment