Skip to content

Instantly share code, notes, and snippets.

@gchaix
Last active October 3, 2018 01:16
Show Gist options
  • Save gchaix/db47ffe9394191dafead to your computer and use it in GitHub Desktop.
Save gchaix/db47ffe9394191dafead to your computer and use it in GitHub Desktop.
Example Varnish default.vcl for Drupal
#
# Customized VCL file for serving up a Drupal site with multiple back-ends.
#
# Based on this excellent Lullabot article:
# http://www.lullabot.com/articles/varnish-multiple-web-servers-drupal
#
# Hooks for New Relic
C{
#include <sys/time.h>
#include <stdio.h>
}C
# Define the list of backends (web servers).
# status.php contains "<?php echo $_SERVER['SERVER_NAME'] . ' - ' . date(DATE_RFC822); ?>"
backend web1 {
.host = "192.168.1.1";
.port = "8080";
.probe = { .url = "/status.php"; .interval = 15s; .timeout = 3s; .window = 5;.threshold = 3; }
}
backend web2 {
.host = "192.168.1.2";
.port = "8080";
.probe = { .url = "/status.php"; .interval = 15s; .timeout = 3s; .window = 5;.threshold = 3; }
}
backend web3 {
.host = "192.168.1.3";
.port = "8080";
.probe = { .url = "/status.php"; .interval = 15s; .timeout = 3s; .window = 5;.threshold = 3; }
}
backend web4 {
.host = "192.168.1.4";
.port = "8080";
.probe = { .url = "/status.php"; .interval = 15s; .timeout = 3s; .window = 5;.threshold = 3; }
}
backend web5 {
.host = "192.168.1.5";
.port = "8080";
.probe = { .url = "/status.php"; .interval = 15s; .timeout = 3s; .window = 5;.threshold = 3; }
}
# Define the director that determines how to distribute incoming requests.
director default_director client {
{
.backend = web1;
.weight = 1;
}
{
.backend = web2;
.weight = 1;
}
{
.backend = web3;
.weight = 1;
}
{
.backend = web4;
.weight = 1;
}
{
.backend = web5;
.weight = 1;
}
}
# Respond to incoming requests.
sub vcl_recv {
# New Relic
C{
struct timeval detail_time;
gettimeofday(&detail_time,NULL);
char start[20];
sprintf(start, "t=%lu%06lu", detail_time.tv_sec, detail_time.tv_usec);
VRT_SetHdr(sp, HDR_REQ, "\020X-Request-Start:", start, vrt_magic_string_end);
}C
# set identity based on cookie so sesisons stick on backends
set client.identity = req.http.cookie;
set req.backend = default_director;
# Use anonymous, cached pages if all backends are down.
if (!req.backend.healthy) {
unset req.http.Cookie;
}
# Allow the backend to serve up stale content if it is responding slowly.
set req.grace = 6h;
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;
}
}
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
# Do not cache these paths.
if (req.url ~ "^/status\.php$" ||
req.url ~ "^/cron\.php.*$" ||
req.url ~ "^/update\.php$" ||
req.url ~ "^/ooyala/ping$" ||
req.url ~ "^/admin/build/features" ||
req.url ~ "^/info/.*$" ||
req.url ~ "^/flag/.*$" ||
req.url ~ "^.*/ajax/.*$" ||
req.url ~ "^.*/ahah/.*$") {
return (pass);
}
if (req.http.Authorization || req.http.Authenticate)
{
return (pass);
}
# Pipe these paths directly to Apache for streaming.
if (req.url ~ "^/admin/content/backup_migrate/export") {
return (pipe);
}
# Do not allow outside access to cron.php or install.php.
if (req.url ~ "^/(cron|install)\.php$") {
# Have Varnish throw the error directly.
error 404 "Page not found.";
# Use a custom error page that you've defined in Drupal at the path "404".
# set req.url = "/404";
}
# Handle compression correctly. Different browsers send different
# "Accept-Encoding" headers, even though they mostly all support the same
# compression mechanisms. By consolidating these compression headers into
# a consistent format, we can reduce the size of the cache and get more hits.=
# @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") {
# If the browser supports it, we'll use gzip.
set req.http.Accept-Encoding = "gzip";
}
else if (req.http.Accept-Encoding ~ "deflate") {
# Next, try deflate if it is supported.
set req.http.Accept-Encoding = "deflate";
}
else {
# Unknown algorithm. Remove it and send unencoded.
unset req.http.Accept-Encoding;
}
}
# Always cache the following file types for all users.
if (req.url ~ "\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-zA-Z0-9]+)?$") {
unset req.http.Cookie;
return(lookup);
}
# Tell Varnish to cache Drupal image derivatives with ?itok= introduced in Drupal 7.20
if (req.url ~ "(\?itok=)([a-zA-Z0-9]+)?$") {
unset req.http.Cookie;
return(lookup);
}
# Remove all cookies that Drupal doesn't need to know about. ANY remaining
# cookie will cause the request to pass-through to Apache. For the most part
# we always set the NO_CACHE cookie after any POST request, disabling the
# Varnish cache temporarily. The session cookie allows all authenticated users
# to pass through as long as they're logged in.
if (req.http.Cookie) {
set req.http.Cookie = ";" + req.http.Cookie;
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|NO_CACHE|oldIE)=", "; \1=");
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
if (req.http.Cookie == "") {
# If there are no remaining cookies, remove the cookie header. If there
# aren't any cookie headers, Varnish's default behavior will be to cache
# the page.
unset req.http.Cookie;
}
else {
# If there is any cookies left (a session or NO_CACHE cookie), do not
# cache the page. Pass it on to Apache directly.
return (pass);
}
}
# Force a lookup so Varnish doesn't fall through to the default behavior before doing a lookup
return (lookup);
}
# Routine used to determine the cache key if storing/retrieving a cached page.
sub vcl_hash {
# Include cookie in cache hash.
# This check is unnecessary because we already pass on all cookies.
# if (req.http.Cookie) {
# set req.hash += req.http.Cookie;
# }
# Uncomment if different languages are served at the same URL.
# if( req.http.Accept-Language ) {
# set req.hash += req.http.Accept-Language;
# }
}
# Code determining what to do when serving items from the Apache servers.
sub vcl_fetch {
# Don't allow static files to set cookies.
if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$") {
# beresp == Back-end response from the web server.
unset beresp.http.set-cookie;
set beresp.http.cache-control = "public";
}
# No cookies on Drupal 7.21 image derivatives
if ( req.url ~ "(\?itok=)([a-zA-Z0-9]+)?$") {
unset beresp.http.set-cookie;
}
# Saint mode
if (beresp.status == 500) {
set beresp.saintmode = 20s;
if (req.request != "POST") {
return(restart);
} else {
error 500 "Failed";
}
}
if (beresp.status == 503) {
set beresp.saintmode = 20s;
if (req.request != "POST") {
return(restart);
} else {
error 503 "Failed";
}
}
set beresp.grace = 6h;
# Sometimes Drupal sets no-cache headers, we still want Varnish to cache those pages
if (beresp.http.cache-control ~ "max-age=0, no-cache") {
/* Remove Expires from backend, it's not long enough */
unset beresp.http.expires;
/* Set the clients TTL on this object */
set beresp.http.cache-control = "max-age=60";
/* Set how long Varnish will keep it */
set beresp.ttl = 1d;
/* marker for vcl_deliver to reset Age: */
set beresp.http.magicmarker = "1";
}
}
# In the event of an error, show friendlier messages.
sub vcl_error {
# Redirect to some other URL in the case of a homepage failure.
#if (req.url ~ "^/?$") {
# set obj.status = 302;
# set obj.http.Location = "http://backup.example.com/";
#}
# Otherwise redirect to the homepage, which will likely be in the cache.
set obj.http.Content-Type = "text/html; charset=utf-8";
synthetic {"
<html>
<head>
<title>Page Unavailable</title>
<style>
body { background: #303030; text-align: center; color: white; }
#page { border: 1px solid #CCC; width: 500px; margin: 100px auto 0; padding: 30px; background: #323232; }
a, a:link, a:visited { color: #CCC; }
.error { color: #222; }
</style>
</head>
<body onload="setTimeout(function() { window.location = '/' }, 5000)">
<div id="page">
<h1 class="title">Page Unavailable</h1>
<p>The page you requested is temporarily unavailable.</p>
<p>We're redirecting you to the <a href="/">homepage</a> in 5 seconds.</p>
<div class="error">(Error "} + obj.status + " " + obj.response + {")</div>
</div>
</body>
</html>
"};
return (deliver);
}
sub vcl_deliver {
# return (deliver);
if (resp.http.magicmarker) {
unset resp.http.magicmarker;
set resp.http.age = "0";
}
#add cache hit data
if (obj.hits > 0) {
#if hit add hit count
set resp.http.X-Cache = "HIT";
set resp.http.X-Cache-Hits = obj.hits;
} else {
set resp.http.X-Cache = "MISS";
}
set resp.http.X-Varnish-Debug-Hits = obj.hits;
set resp.http.X-Varnish-Debug-Age = resp.http.age;
}
sub vcl_pipe {
# http://www.varnish-cache.org/ticket/451
# This forces every pipe request to be the first one.
set bereq.http.connection = "close";
}
@benhosmer
Copy link

Thanks Greg! It looks like you cache some admin pages? How do you keep the cookies out that would ordinarily prevent them from being cached?

@avblink
Copy link

avblink commented Nov 16, 2016

I wander if you could help.

I'm using multilingual support on drupal 7 site with language_cookies module.

Language is correctly set when running without varnish but with varnish it always sets language cookie to the default language.

Do you know if there is any varnish config that needs to be added for languages to be read from cookies?

Thank you very much

Alex

@leymannx
Copy link

Really cool, thanks for sharing. I only wish you'd stick to one coding style. For readability. Like in the default.vcl https://github.com/mattiasgeniar/varnish-4.0-configuration-templates/blob/master/default.vcl

Tab size: 2 spaces
Indent: 2 spaces
Continuation indent: 4 spaces

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment