Skip to content

Instantly share code, notes, and snippets.

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
# 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 = "";
.port = "8080";
.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.
if (req.backend.healthy) {
set req.grace = 30s;
} else {
set req.grace = 1h;
# Normalize Accept-Encoding to prevent duplicates in the cache
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") {
# Don't cache POST, PUT, or DELETE requests
if (req.request == "POST" || req.request == "PUT" || req.request == "DELETE") {
# 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.
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";
# 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"
) {
# 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;
# Do not deliver into cache otherwise.
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";
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.

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