Skip to content

Instantly share code, notes, and snippets.

@guilhermeblanco
Created June 13, 2014 15:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guilhermeblanco/1794c9d1f943510834b4 to your computer and use it in GitHub Desktop.
Save guilhermeblanco/1794c9d1f943510834b4 to your computer and use it in GitHub Desktop.
# 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);
}
@antonbabenko
Copy link

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:

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, ";(PHPSESSID|REMEMBERME)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    if (req.http.Cookie == "") {
      unset req.http.Cookie;
    }
  }

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