Question

Varnish default behaviour is to never lookup for requests containing a Cookie header. In other words, a request containing a Cookie header will never be cached. I need to override this behaviour to just ignore requests with a Cookie header.

Consider the next user behaviour in my application:

  1. User enters application home page (/), page should be cached, backend returns a public cache-control, everything is fine, page gets cached by Varnish.
  2. User navigates to a custom page (/not-cacheable) which is not cacheable, backend returns a private cache-control. Also returns a Set-Cookie header in the response. Varnish ignores this request and the user ends up with a cookie. So far so good.
  3. User navigates back to the home page (/), which, remember, is already cached. Problem is, the user request now carries a Cookie header. This causes Varnish to ignore the request and delegate to the backend.

Deleting the Cookie won't work because when the user returns to the /not-cacheable route, he won't see his personalized page, as the Cookie header has been striped out. Instead, the backend returns a newly generated session with a new id in the Set-Cookie.

Also, having every Cookie request to lookup in Varnish caused every request, regarding the method, or the backend response, to be cached.

If there was some way of telling Varnish to just ignore the Cookie header, this way I could cache requests with that header, by letting the backend decide if the request should be cacheable or not.

Any ideas?

Was it helpful?

Solution

Just for the record, I finally came up with a VCL script that solves this problem using custom headers and restarting the request:

backend default
{
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv
{
    if(req.http.X-Force-Backend)
    {
        return(pass);
    }

    if(req.http.Cookie && req.request ~ "(GET|HEAD)")
    {
        set req.http.X-Cookie = req.http.Cookie;
        remove req.http.Cookie;

        return(lookup);
    }
}

sub vcl_deliver
{
    if(resp.http.Cache-control ~ "(private|no-cache|no-store)" 
        && !req.http.X-Force-Backend
        && req.request ~ "(GET|HEAD)"
    )
    {
        set req.http.X-Force-Backend = "YES";
        set req.http.Cookie = req.http.X-Cookie;

        remove req.http.X-Cookie;

        return(restart);
    }
}

As you can see, by always deleting the Cookie header and restarting the request when we have a non-cacheable response from the backend, we can achieve the desired effect. I don't know about possible performance drawbacks, but I'm using it in production and it works quite well.

I also wrote a blog post about this specific problem, if anyone is using Symfony and Varnish aswell, it might be interesting: http://albertofem.com/post/symfony-varnish-and-http-practical-considerations.html

OTHER TIPS

I think removing the cookie header in the vcl_recv should do the trick, here's something I've used on my wordpress to ignore front end but not backend, you can easily remove the if condition to make it over the whole server

sub vcl_recv {
  if (req.url !~ "^/wp-"){
    unset req.http.cookie;
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top