The vary header is a response header used to create different variations of an object in the cache. In this post I will show how the vary header works, how we can use it and more importantly ways not to use the header.
How the vary header works
The most widely used use case for the vary header is to vary by the Accept-Encoding
header. In this use case, the response returned would be different depending on what Content-Encoding
the browser can support. In this case the response header sent by the origin would look something like the following:
vary: Accept-Encoding
To generate the response, the cache will compare the Accept-Encoding request header value with the content-encoding
returned by the origin
# request header:
Accept-Encoding: gzip, deflate
# response header:
content-encoding: gzip
In this case we can see the browser supports gzip
and the resource stored in cache is of type gzip
so we are able to return this without further processing.
Where the vary header does not work well
Issues with caches supporting the vary header come from more advanced use-cases. Here’s an example where we cache variations on the Accept-Language
header on top of the previous Accept-Encoding
header.
vary: Accept-Language Accept-Encoding
This would allow us to serve different versions to say English and French speakers based on what their Accept-Language header is:
Accept-Language: en
Accept-Language: fr
The problems come from the fact there are many different ways of browsers saying the same thing. All of the below examples are where English should be returned:
en
en-us
en-US,en;q=0.5
en-US
en-US,en;q=0.8
en-US;q=0.8,en;q=0.6,fr-CA,fr;q=0.4
The last one is an example of one where the user speaks both English and French but prefers speaking English.
Issues with vary
Generally browsers ignore the vary header while caching. This is because the vary header rarely changes for an individual browser. The issues with vary appear when we want to implement a cache in front of our website.
In the last example we can see there are multiple ways of requesting the same resource. All of these should return the same object.If we were to use the vary header value as part of our cache key it would end up storing many copies of the same file because there would be no way of knowing for sure if the correct language was returned. Additionally the first one of these requests would go to the origin instead of being cached.
vary is not a key but an algorithm for generating a key
In terms of a CDN, a good way of thinking about vary is to think of it as an algorithm for generating a key rather than a key itself. In the Accept-Language
example above there were far too many ways of saying the same thing that to use whatever was in the header as a key would mean the cache performance would be massively impacted. We’d have many requests for the origin even though we already had the object in the cache.
Instead of just using the vary header value as a cache key we would have to execute some logic to check if our cached response is valid for the request. In the example above, the logic would return en
for all values in the list and this could then be used as part of the cache key.
How to use the vary header
The TLDR of this is try find another way of using the vary header if possible, especially if using a third party CDN. Below is a list of CDN providers and their support for vary:
- Fastly: yes
- Cloudflare: Images only - must enable the “vary for images” product
- Amazon Cloudfront: Only certain headers
- Google Media CDN: Only certain headers
As you can see only Fastly supports vary as per the spec but they have taken great pains to ensure you do not do something bad with the header. The reason for this is because there is a high chance vary will split the cache resulting in worse performance for your site and an increased volume of files being stored in the CDN. The CDN providers that provide a limited support for vary do this so they can control what gets stored. In the
Accept-Language
example, the CDN provider could normalise the values so what gets passed to the cache key for anyone requesting an English language site is justen
. However this normalisation is hidden from the customer and can change depending on site to site so it is easier for them to just not implement vary support in the first place. Fastly allow anyone to program their own normalisation so it is up to you to implement support for vary.
If you do want to have different pages based on language for your site, it is best practice to use different domains like example.com/en/blog rather than using a single domain and varying on the language. Similarly mobile and desktop sites are better done the same way with different domains.
Summary
I’ve gone through here how to use the vary header and the problems with using it. Generally if you want to vary based on this header then you must consider the impact to cache performance it will have. You will also have to consider what CDN provider to use. At this time your options are limited to just Fastly for full support.
The limited support for vary headers will become more problematic going forward as certain frameworks such as react are using vary in new features like React Server Components to store different versions of the same object.