Skip to content

Instantly share code, notes, and snippets.

@bmarini
Created June 30, 2011 18:01
Show Gist options
  • Save bmarini/1056794 to your computer and use it in GitHub Desktop.
Save bmarini/1056794 to your computer and use it in GitHub Desktop.
A good varnish config for a Rails app
# https://www.varnish-cache.org/docs/2.1/tutorial/vcl.html
# https://www.varnish-cache.org/trac/wiki/VCLExamples
# Summary
# 1. Varnish will poll the backend at /health_check to make sure it is
# healthy. If the backend goes down, varnish will server stale content
# from the cache for up to 1 hour.
# 2. Varnish will pass X-Forwarded-For headers through to the backend
# 3. Varnish will remove cookies from urls that match static content file
# extensions (jpg, gif, ...)
# 4. Varnish will normalize the Accept-Encoding header
# 5. Varnish will respect the Cache-Control header, even when a Set-Cookie
# header accompanies it from the backend. For example,
# `Cache-Control: no-cache` will not get stored in Varnish and
# `Cache-Control: max-age=600` will.
# 6. Varnish will add a X-Varnish-Cache header to aid in debugging
backend default {
.host = "127.0.0.1";
.port = "8080";
# https://www.varnish-cache.org/trac/wiki/BackendPolling
.probe = {
.url = "/health_check";
.timeout = 2 s;
.interval = 1s;
.window = 10;
.threshold = 8;
}
}
# NOTE: vcl_recv is called at the beginning of a request, after the complete
# request has been received and parsed. Its purpose is to decide whether or not
# to serve the request, how to do it, and, if applicable, which backend to use.
sub vcl_recv {
# If the backend is healthy, only accept objects that are 30 seconds old,
# but if the backend is sick, accept objects that are up to an hour old.
# https://www.varnish-cache.org/trac/wiki/VCLExampleGrace
if (req.backend.healthy) {
set req.grace = 30s;
} else {
set req.grace = 1h;
}
# Normalize Accept-Encoding to prevent duplicates in the cache
# https://www.varnish-cache.org/trac/wiki/VCLExampleNormalizeAcceptEncoding
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
# unkown algorithm
remove req.http.Accept-Encoding;
}
}
# This rule is to insert the client's ip address into the request header
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;
}
}
# Force lookup if the request is a no-cache request from the client
# IE - shift-reload causes cache refresh - We may not want this in
# production but useful during initial deployment and testing
if (req.http.Cache-Control ~ "no-cache") {
purge_url(req.url);
}
# Don't cache POST, PUT, or DELETE requests
if (req.request == "POST" || req.request == "PUT" || req.request == "DELETE") {
return(pass);
}
# Strip cookies from static content
if (req.request == "GET" && req.url ~ "\.(png|gif|jpg|swf|css|js)$") {
unset req.http.cookie;
}
# We will try to retrieve every request from the cache. There will be no
# intelligence on the varnish side to determine whether to look or not look
# at the cache.
return(lookup);
}
sub vcl_pipe {
# Note that only the first request to the backend will have
# X-Forwarded-For set. If you use X-Forwarded-For and want to
# have it set for all requests, make sure to have:
# set bereq.http.connection = "close";
# here. It is not set by default as it might break some broken web
# applications, like IIS with NTLM authentication.
set bereq.http.connection = "close";
return(pipe);
}
# NOTE: vcl_fetch is called after a document has been successfully retrieved
# from the backend. Normal tasks her are to alter the response headers, trigger
# ESI processing, try alternate backend servers in case the request failed.
sub vcl_fetch {
# Keep objects 1 hour in cache past their expiry time. This allows varnish
# to server stale content if the backend is sick.
set beresp.grace = 1h;
# If header specifies "no-cache", don't cache.
if (
beresp.http.Pragma ~ "no-cache" ||
beresp.http.Cache-Control ~ "no-cache" ||
beresp.http.Cache-Control ~ "private"
) {
return(pass);
}
# If header specifies "max-age", remove any cookie and deliver into the cache.
# The idea here is to trust the backend. If the backend set a max-age in
# the Cache-Control header, then the response should be cached even if there
# is a Set-Cookie header. The cleaner way to handle this is the not set a
# Set-Cookie header in the backend, but unfortunately Rails always sets one.
if (beresp.http.Cache-Control ~ "max-age") {
unset beresp.http.Set-Cookie;
return(deliver);
}
# Do not deliver into cache otherwise.
return(pass);
}
sub vcl_deliver {
# The below provides custom headers to indicate whether the response came from
# varnish cache or directly from the app.
if (obj.hits > 0) {
set resp.http.X-Varnish-Cache = "HIT";
} else {
set resp.http.X-Varnish-Cache = "MISS";
}
}
@ethier
Copy link

ethier commented Oct 23, 2013

Thanks for sharing. I'm going over this now to test it out with a Rails 3.2.15 application. I've forked it to make some minor changes to support Varnish 3.

@rapind
Copy link

rapind commented Mar 2, 2014

Thanks for this!

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