Created
June 13, 2014 15:45
-
-
Save guilhermeblanco/1794c9d1f943510834b4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# VCL configuration file for Varnish | |
# Define which IP addresses or hosts have access to files that are | |
# blocked from the public internet | |
acl internal | |
{ | |
"localhost"; | |
"10.0.2.15"; | |
"192.168.56.1"; | |
"<%= @hostname %>"; | |
"<%= @ipaddress %>"; | |
} | |
# Define origin probe mechanism | |
probe favicon | |
{ | |
.url = "/favicon.ico"; | |
.timeout = 60ms; | |
.interval = 5s; | |
.window = 5; | |
.threshold = 3; | |
} | |
# Define origin web servers | |
backend application | |
{ | |
.host = "<%= @ipaddress %>"; | |
.port = "<%= @app_port %>"; | |
.probe = favicon; | |
} | |
# Normalizes URL | |
sub normalize_url | |
{ | |
# Normalize requests sent via curl -X mode and LWP | |
if (req.url ~ "^http://") { | |
set req.url = regsub(req.url, "http://[^/]*", ""); | |
} | |
} | |
# Creates an X-Forwarded-For and X-Forwarded-Port if not present. | |
# If present, append the current client IP | |
sub normalize_x_forwarded | |
{ | |
if (req.restarts == 0) { | |
# Must be Varnish port to correctly render ports on Symfony2 | |
set req.http.X-Forwarded-Port = "<%= @port %>"; | |
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; | |
} | |
} | |
} | |
# Handle compression correctly. Different browsers send different "Accept Encoding" | |
# headers, even though they mostly support the same compression mechanisms. By | |
# consolidating these headers into a consistent format, we can reduce the size of the | |
# cache and get more hits. | |
sub normalize_accept_encoding | |
{ | |
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 { | |
# Unknown algorithm | |
unset req.http.Accept-Encoding; | |
} | |
} | |
} | |
# Some backends send Vary: User-Agent. This is usually a bad idea, since you end up with many, | |
# many copies of what's usually the same content in the cache. | |
sub normalize_vary | |
{ | |
if (req.http.Vary && req.http.Vary ~ "User-Agent") { | |
set req.http.Vary = regsub(req.http.Vary, "(^|; ) *User-Agent,? *", "\1"); | |
if (req.http.Vary == "") { | |
remove req.http.Vary; | |
} | |
} | |
} | |
# Remove cookies that may interfere with request cache handling | |
sub normalize_cookie | |
{ | |
# Remove the "has_js" cookie | |
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)[^;]*", ""); | |
# Remove any Google Analytics based cookies | |
set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", ""); | |
set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", ""); | |
set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", ""); | |
# Remove possible remaining garbage | |
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", ""); | |
# Are there cookies left with only spaces or that are empty? | |
if (req.http.cookie ~ "^ *$") { | |
unset req.http.cookie; | |
} | |
} | |
# Handle incoming requests | |
# | |
# vcl_recv (yes, we're skimpy with characters, it's Unix) 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. | |
# In vcl_recv you can also alter the request. Typically you can alter the | |
# cookies and add and remove request headers. | |
# | |
# Note that in vcl_recv only the request object, req is available. | |
sub vcl_recv | |
{ | |
# Allow purge only from internal users | |
if (req.request == "PURGE") { | |
if ( ! (client.ip ~ internal)) { | |
error 405 "Not allowed" + client.ip; | |
} | |
return (lookup); | |
} | |
call normalize_url; | |
call normalize_x_forwarded; | |
call normalize_accept_encoding; | |
call normalize_vary; | |
call normalize_cookie; | |
# Skip requests that are non-cacheable | |
if (req.http.Pragma ~ "no-cache" || req.http.Cache-Control ~ "(no-cache|no-store|private)") { | |
return (pass); | |
} | |
# Skip assets | |
if (req.url ~ "\.(ico|png|gif|jpe?g|swf|sstm|gz|zip|tgz|woff)") { | |
return (pass); | |
} | |
# Define grace period if backend is not behaving correctly. This turns Varnish into | |
# grace mode which will return cached content even through the application server may | |
# be acting inconsistently or slowly. | |
if ( ! req.backend.healthy) { | |
set req.grace = 4h; | |
} | |
# Notify about ESI support | |
set req.http.Surrogate-Capability = "symfony2=ESI/1.0"; | |
# Pipe the request to backend if not a valid RFC-2616 HTTP method | |
if (req.request != "GET" && | |
req.request != "HEAD" && | |
req.request != "PUT" && | |
req.request != "POST" && | |
req.request != "TRACE" && | |
req.request != "OPTIONS" && | |
req.request != "DELETE") { | |
return (pipe); | |
} | |
# GET and HEAD are the only HTTP requests that Varnish handles by default | |
if (req.request != "GET" && req.request != "HEAD") { | |
return (pass); | |
} | |
return (lookup); | |
} | |
# Determine the cache key when storing/retrieving a cached page | |
sub vcl_hash | |
{ | |
# Include Host/IP in hash | |
if (req.http.Host) { | |
hash_data(req.http.Host); | |
} else { | |
hash_data(server.ip); | |
} | |
hash_data(req.url); | |
return (hash); | |
} | |
# Hit backend server handling | |
sub vcl_hit | |
{ | |
# Purge entry | |
if (req.request == "PURGE") { | |
purge; | |
error 200 "Purged"; | |
} | |
} | |
# Missed handling | |
sub vcl_miss | |
{ | |
# Purge entry | |
if (req.request == "PURGE") { | |
purge; | |
error 200 "Purged"; | |
} | |
} | |
# Handle backend fetched responses | |
# | |
# 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. | |
# | |
# In vcl_fetch you still have the request object, req, available. There is also | |
# a backend response, beresp. beresp will contain the HTTP headers from the | |
# backend. | |
sub vcl_fetch | |
{ | |
# Turn on ESI handling and compress the response by default | |
if (beresp.http.Surrogate-Control ~ "ESI/1.0") { | |
unset beresp.http.Surrogate-Control; | |
set beresp.do_gzip = true; | |
set beresp.do_esi = true; | |
} | |
if (beresp.status >= 300) { | |
return (hit_for_pass); | |
} | |
# Skip caching in case headers tell to not do it | |
if (beresp.http.Pragma ~ "no-cache" || beresp.http.Cache-Control ~ "(no-cache|no-store|private)") { | |
return (hit_for_pass); | |
} | |
# Do not allow static files to set cookies | |
if (req.url ~ "\.(png|gif|jpe?g|swf|sstm|css|scss|sass|js|gz|zip|tgz|woff)") { | |
unset beresp.http.Set-Cookie; | |
set beresp.do_gzip = true; | |
set beresp.ttl = 0s; | |
} | |
# Enforce gzip compression on assets | |
if (req.url ~ "\.(html?|css|scss|sass|js)") { | |
unset beresp.http.Set-Cookie; | |
set beresp.do_gzip = true; | |
} | |
# Mark as "Hit-For-Pass" for the next 2 minutes | |
if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") { | |
set beresp.ttl = 120s; | |
return (hit_for_pass); | |
} | |
return (deliver); | |
} | |
# Handle outgoing responses | |
sub vcl_deliver | |
{ | |
# Identify which Varnish handled the request | |
if (obj.hits > 0) { | |
set resp.http.X-Varnish-Cache = "HIT " + obj.hits; | |
} else { | |
set resp.http.X-Varnish-Cache = "MISS"; | |
} | |
unset resp.http.Via; | |
unset resp.http.Age; | |
unset resp.http.Server; | |
unset resp.http.Server; | |
unset resp.http.X-Varnish; | |
return (deliver); | |
} | |
# Handle error responses | |
sub vcl_error | |
{ | |
# Sometimes Varnish enters in I/O limit and trigger errors, attempting to restart | |
# Under "Bad Gateway", attempt to restart up to three times | |
if (req.restarts == 0 || (obj.status == 503 && req.restarts < 3)) { | |
return (restart); | |
} | |
set obj.http.Content-Type = "text/html; charset=utf-8"; | |
set obj.http.Retry-After = "5"; | |
synthetic {" | |
<?xml version="1.0" encoding="utf-8"?> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | |
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html> | |
<head> | |
<title>"} + obj.status + " " + obj.response + {"</title> | |
</head> | |
<body> | |
<h1>Error "} + obj.status + " " + obj.response + {"</h1> | |
<p>"} + obj.response + {"</p> | |
<h3>Guru Meditation:</h3> | |
<p>XID: "} + req.xid + {"</p> | |
<hr> | |
<p>Varnish cache server</p> | |
</body> | |
</html> | |
"}; | |
return (deliver); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Instead of removing specific cookies (
sub normalize_cookie
) it is better to whitelist cookies your app cares about, I do it like this in Varnish 4: