The Varnish Cache is a stable, high-performance web-application accelerator. It functions as a reverse-proxy to the application server, reducing load and improving performance.

How it Works

Varnish sits between your application servers and the Internet. The intent here is not focused on security but on performance. Varnish can sanatise inputs (eg Cookies) and, through a clever hashing mechanism, cache pages in a session specific way.

Varnish Cache Layout

The public HTTP requests go directly to Varnish, which may (likely) serve from cache. Else, the requests are sent to the web-servers, which may hit the database.

Installing Varnish

Varnish is very easy to install and has very little dependencies

~ # emerge www-servers/varnish
[ebuild  N     ] www-servers/varnish-2.0.4-r1  769 kB

Configuring Varnish

Varnish is configured through a VCL file, which is a definition of VCL functions which operate on the request. This file defines what will happen when a request is received, how it's handled (lookup, pipe, pass), how it's hashed and how it's modified on input and output.

Here is an annotated varnish cache vcl file

# We define a back-end server to fetch from
backend default {
	.host = "10.0.0.8";
	.port = "80";
	.connect_timeout = 16s;
	.first_byte_timeout = 96s;
	.between_bytes_timeout = 8s;
}

It's very important to familiarise yourself with the VCL Basics, study the diagram on that page.

vcl_recv

The vcl_recv function is called for every inbound request, you will decided to pass, pipe or lookup the request in the cache.

// And Here we sanatise the cookie; so our app&cache only ever sees reasonable stuff
sub vcl_recv {

	if (req.url ~ "/static") {
		unset req.http.Cookie;
	}

	if (req.http.User-Agent ~ "Android|iPad|iPhone|Mobile") {
		set req.http.X-Device = "mobile";
	} else {
		set req.http.X-Device = "desktop";
	}

	if (req.request == "GET" || req.request == "HEAD") {
		req.http.X-Debug = "true";
		return (lookup);
	}

	return (pipe);

}

vcl_hash

When the connection is passed to the cache it's key is made up in the vcl_hash routine. We addeded a special Device header, which we want to use for the caching.

sub vcl_hash {
	hash_data(req.url);
	hash_data(req.http.X-Device);
	return(hash);
}

vcl_pipe

For many other connections we just pass directly to the backend via pipe, we want to make sure they terminate.

sub vcl_pipe {
	set bereq.http.connection = "close";
}

vcl_deliver

On delivery some times we set some debug output headers. I also like to strip out, or re-write others.

sub vcl_deliver {

	set resp.http.Server = "Magic Carpet";
	set resp.http.X-Humans = "Apply for Jobs at http://ars.io/j/e";
	set resp.http.Vary = "Accept-Encoding, Cookie, User-Agent";

	if (req.http.X-Debug) {
		if (obj.hits > 0) {
			set resp.http.X-Cache = obj.hits;
		}

		if (req.http.X-Cache-Detail) {
			set resp.http.X-Cache-Detail  = req.http.X-Cache-Detail;
		}

		if (req.http.X-Cache-TTL) {
			set resp.http.X-Cache-TTL = req.http.X-Cache-TTL;
		}

		if (req.http.X-Device) {
			set resp.http.X-Device = req.http.X-Device;
		}
	}

	unset resp.http.Age;
	unset resp.http.Via;
	unset resp.http.X-Varnish;

}

See Also