Recommendations for altering VCL hash?


#1

Are there any guidelines or best practices to keep in mind if I need to alter the Varnish hash for my service?


#2

There are a few things to keep in mind:

  1. If you can avoid altering vcl_hash, do so.
    Vary: is more often than not a better alternative. The key being that if you have different versions for the same URL, you usually still want to purge them all at once when purging that URL. For more info, see my blog post about Vary.

  2. Make your changes on a test service first, and test thoroughly.
    Messing up vcl_hash can cause the wrong page to be served from cache, and lead to a lot of confusion amongst users and people dealing with the fall-out.

  3. If you want “purge all” to work, include #FASTLY hash in your vcl_hash. For example:

    sub vcl_hash {
    #FASTLY hash
      set req.hash += req.url;
      return(hash);
    }
    

    If you do not, purge all will simply do nothing.

    (The example above only has req.url and not req.http.Host, meaning that the Host: header is no longer used in making the hash, and thus http://example.com/foo and http://www.example.com/foo will be a single object in our cache.)

  4. Unless you’re doing something really advanced, make sure you at the very least use req.url somewhere in vcl_hash, even if only as an argument to regsub().

    For example:

    sub vcl_hash {
    #FASTLY hash
      set req.hash += req.http.host;
      if (req.url ~ "^/api/foo.json\?") {
        set req.http.temp = "/api/foo.json?" regsub(req.url, "^.*[?&](myparam=[^&]*).*$", "\1");
        set req.hash += req.http.temp;
      } else {
        set req.hash += req.url;
      }
      return(hash);
    }
    

    This will make Varnish ignore all query string parameters except myparam for URLs that start with /api/foo.json?. You might do this if you want to leave req.url intact for logging or restart purposes.

  5. As mentioned earlier, purging is affected, since we purge by the hash. So if you add something to the hash, like say, a header, make sure you account for that header when sending a purge to Fastly. (Or use Surrogate-Key purging, see http://docs.fastly.com/guides/purging/how-can-i-purge-content-on-fastly)

    If you add set req.hash += req.http.Accept-Encoding for instance (which Fastly normalizes to be either gzip, deflate, or not present) you would have to send 3 separate purges. One with Accept-Encoding: gzip, one with Accept-Encoding: deflate, and one without any Accept-Encoding header.

    (This is where using the Vary: header really shines. Having Vary: Accept-Encoding in the responses would allow you to have all 3 variations of an object in the cache, and purge them with a single request.)

  6. Make sure to include return(hash). If you do not, the function will fall through to the default VCL built into varnish, which for vcl_hash will add req.url and req.http.Host to the hash.

    Although, if you wanted to, you could write a really short vcl_hash function by relying on that. For instance:

    sub vcl_hash {
    #FASTLY hash
      set req.hash += req.http.Accept-Encoding;
    }
    

    This function will work just fine. However, we do advise not to rely on implicit things like that, as it might confuse others reading your VCL.

As vcl_hash is probably the least overridden VCL function, and the easiest to shoot yourself in the foot with, we strongly advise to use extreme caution. You could, for instance, post your proposed vcl_hash function on this forum, and get a little peer review. :smile: