Configuring Varnish to Cache per User Agent in Pressflow

Chris Toler

Written by Chris Toler

Cache icon for representing caching Per User Agent In Pressflow

If you’re familiar with Varnish, you’re well aware of its awesomeness. It can turn your site from a slowpoke into a speed demon. We recently observed a site (which we didn’t build) that was, under load, taking about 800ms to respond per request. In went Varnish, and the response time for 99% of the requests dropped to just over 2ms. Kind of a “Holy crap!” moment.

Sometimes though, you might run into some issues when trying to configure Varnish. One of these instances is when you need it to provide different caches in different circumstances. For this tutorial, we’ll be focusing on configuring Varnish to provide a cache per group of devices, defined by user agent. Why is this important? Well, what if you want a different theme for each of these scenarios? Without adding some mechanism to handle this, Varnish will just cache the first thing it sees and serve it out to all of the devices.

Before we get started, we’ll point out that this tutorial is only going to cover the Varnish config that will establish the different caches. You’ll need to configure your copy of Pressflow to change the theme based on different devices (we recommend Mobile Tools).

Make a backup

Before we start down the road of modifying files, make sure you back things up. It takes 2 seconds and will save you a lot of stress if you accidentally delete something you didn’t mean to.

cd /path/to/varnish/config

cp default.vcl default.vcl.bak

Define the devices with detect_device

How you set this up depends on how many different caches you want. You could have Varnish define two caches (1: Desktop, 2: Everything else), or five. We’re going to tell it to create three… desktop users, smartphone users, and everything else.

Add this code above the “sub vcl_recv” section in your configuration file:

# Detect the device
sub detect_device {
  # Define the desktop device
  set req.http.X-Device = "desktop";

  if (req.http.User-Agent ~ "iP(hone|od)" || req.http.User-Agent ~ "Android" || req.http.User-Agent ~ "iPad") {
    # Define smartphones and tablets
    set req.http.X-Device = "smart";
  }

  elseif (req.http.User-Agent ~ "SymbianOS" || req.http.User-Agent ~ "^BlackBerry" || req.http.User-Agent ~ "^SonyEricsson" || req.http.User-Agent ~ "^Nokia" ||
  req.http.User-Agent ~ "^SAMSUNG" || req.http.User-Agent ~ "^LG") {
    # Define every other mobile device
    set req.http.X-Device = "other";
  }
}

Call detect_device from sub vcl_recv

Now that we have defined our devices, we need to make Varnish respect them as conditions. Add this to the top of your sub vcl_recv section:

Add this code above the “sub vcl_recv” section in your configuration file:

sub vcl_recv
  call detect_device;

#The rest of your recv config can go down here

Define a new hash per valid device type

Varnish creates and stores caches based on a hash value generated in the “sub vcl_hash” section. If we add something there that triggers different hash values based on the device, our per-device caching should start to work.

Add this to the sub vcl_hash section (Add it to the bottom, after the existing hash configuration):

sub vcl_hash {
  #Existing hash configuration

  # And then add the device to the hash (if its a mobile device)
  if (req.http.X-Device ~ "smart" || req.http.X-Device ~ "other") {
    set req.hash += req.http.X-Device; 
  }
}

… and save the file.

Restart Varnish

Now all you need to do is restart Varnish. If you’re using Ubuntu, or Pantheon, you should be able to do something like:

sudo /etc/init.d/varnish restart

And we’re all done! Varnish should now be storing cache separately based on the device being used. If your site gets a lot of traffic, you may want to keep an eye on memory usage just to be safe.

Chris Toler

Written by Chris Toler

Need technology experts?

Contact Us