9 February 2013
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.

Varnish: Remove all Cookies EXCEPT the ones you want
Tagged on:     

2 thoughts on “Varnish: Remove all Cookies EXCEPT the ones you want

  • 27 February 2018 at 13:27
    Permalink

    Thanks, this was interesting though it doesn’t do what the title says. Your VCL either removes all cookies or passes all cookies depending on the presence of some specific cookie. I’m trying to remove all but one cookies from the request, i.e. when that cookie is present I want to pass just that one cookie, not all other as well. I guess it all depends on how your backend code handles the unexpected cookies.

    Reply
    • 27 February 2018 at 18:19
      Permalink

      D’OH! You are correct, twice. My backend ditches the other cookies!

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.