COO-KIES!

COO-KIES!

So, you’ve got a web server. It’s slow.

So, you optimize your app, and implement some caching. It’s faster, but not fast enough.

So, you decide to use Varnish, the popular reverse-proxy web application accelerator. It doesn’t work.

The reason it doesn’t work is that on your web site, you’ve got all kinds of Javascript-based goodies, like Google Analytics, or maybe embedded videos that set tracking cookies, or maybe things like AddThis that also set client-side cookies.

Well, client-side cookies are for the JS on your site; your application doesn’t actually care about them. BUT – and this is an annoying “but” – if those client-side cookies are set for “yourdomain.com”, then with every single request a reader makes to your server, those cookies are sent along.

And that means Varnish won’t cache anything, because it says, “Cookies?! EEK! No caching!”

So, how do you make Varnish happy with only the cookies that you want to keep?

Read on…

At this point, you might be thinking that I’m crazy. And well, you may have a point there. After all, on the Varnish Wiki, you can find the following example code for removing all cookies except the ones you want to keep:

sub vcl_recv {
  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, ";(COOKIE1|COOKIE2)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
    if (req.http.Cookie == "") {
      remove req.http.Cookie;
    }
  }
}

Isn’t that a daisy? Well, sort of…

Even though the above code wasn’t working for me, it should have. It seems the 4th “set” line deletes all cookies that do not have a space between the semicolon and the cookie name, whereas COOKIE1 and COOKIE2 will.

It should have worked. But for some reason, it didn’t.

So, I found a shorter, simpler way that actually does work for me:

# Do not cache if either of our valid cookies are there
if (req.http.Cookie) {
  if (req.http.Cookie ~ "__sess_id_123=" || req.http.Cookie ~ "__sess_sec_123=") {
    return (pass);
  } else {
    # Nuke all other annoying cookies!
    remove req.http.Cookie;
  }
}

All we’re doing above is saying:

  1. Are there cookies set in the request? If yes, continue.
  2. If the list of cookies contains your approved cookies (__sess_id_123 or __sess_sec_123), then return “pass” – i.e. bypass Varnish and send the request straight to the backend, since your user is logged in.
  3. Otherwise, remove all the cookies, because the only cookies left are evil ones that you don’t care about.

That’s it.

Of course, after the above code, you might have other checks, and if nothing else is amiss, you hit the cache by doing a: return (lookup);

Wasn’t that easy? And, it actually works!

Yes, it’s a bit less robust, but then it shouldn’t really matter if your cookie names are sufficiently unique (__sess_id_123 in our example), which they should be anyway.

So, there you have it.

Get Scottie Stuff!