Vary: which request headers change the response
Vary lists the request headers a cache must take into account before reusing a stored response, because the server produced this body based on their values.
At a glance
- Direction
- Response only. It names request headers.
- Defined in
- RFC 9110
- When absent
- The URL alone is the cache key, so one visitor's variant can be served to everyone.
- Almost always needed
- Vary: Accept-Encoding on any response that may be compressed
- Vary: *
- The response is never reusable from cache, whatever else the headers say.
- Cost
- Each listed header multiplies the number of stored variants for one URL.
- CORS case
- Vary: Origin on any response whose Access-Control-Allow-Origin is computed per request
In detail
A cache stores responses under a key. By default that key is the method and the URL, so if the server produced different bodies for the same URL, the cache cannot tell them apart and will hand the wrong one to somebody. Vary adds the named request headers to the key, so a compressed and an uncompressed copy, or an English and a French copy, are stored and matched separately.
Accept-Encoding is the case nearly every site needs. Without it, a cache that stored a gzipped body can serve those bytes to a client that never asked for compression, which arrives as unreadable output rather than as an error. Most reverse proxies add the header for you when they compress, which is precisely why it is easy to forget when you compress in the application instead.
Every header you list divides the cache. Accept-Language across a dozen locales is manageable, but Vary: User-Agent creates a variant per browser build string, which in practice means almost no reuse at all. If the response really does depend on the user agent, decide the variant on the server and encode it in the URL instead.
Vary: Cookie is the quiet one. It is technically correct for a personalised page, but as almost every visitor has a different cookie, the effective hit rate at a shared cache falls to zero. For personalised responses, Cache-Control: private is usually the honest answer rather than a Vary that pretends the response is shareable.
Examples
Content-Encoding: gzip
Vary: Accept-EncodingAccess-Control-Allow-Origin: https://app.example.com
Vary: OriginWithout this line a shared cache can hand the allow header for one origin to a page on another, and the request fails for reasons that appear nowhere in your logs.
Vary: *Things that catch people out
- Forgetting Vary: Accept-Encoding on a compressed response is a real corruption bug, not a performance one. Some clients receive bytes they cannot decode.
- Vary: User-Agent makes a shared cache nearly useless because the header is close to unique per visitor.
- Some content delivery networks ignore or rewrite Vary on their own terms, so confirm behaviour at the edge rather than assuming the header travels intact.
Try it: HTTP Headers Checker
Check a live URL or paste headers you already have. It grades six security headers from A+ to F and lists every other header the response returned.
HTTP Headers Checker
LOCAL · Browser-OnlyView HTTP response headers for any URL
Enter any website URL to fetch and analyze its HTTP response headers. We'll automatically add https:// if needed.
Why Check Security Headers?
Security headers protect your website against common attacks like XSS, clickjacking, and MIME sniffing. They're set by your web server and cost nothing to implement. Missing security headers are one of the most common findings in security audits.
Related HTTP headers
All http headers →Cache-Control: directives for browsers and shared caches
Every Cache-Control directive that matters, the difference between no-cache and no-store, and how to cache immutable assets forever without breaking deploys.
Accept-Encoding: negotiating gzip, br and zstd
Which compression tokens clients send, how identity and the wildcard behave, why the response needs Vary, and how to test whether compression is really on.
Access-Control-Allow-Origin: who may read this response
How the allow origin header is matched, why a wildcard fails with credentials, why it takes only one origin, and why Vary: Origin is mandatory when you echo.
Age: how long a cached response has been sitting there
What the Age header counts, how it combines with max-age to give remaining freshness, and how to use it to tell a cache hit from a miss at the edge.
If-None-Match: asking whether anything changed
How If-None-Match produces a 304, why it uses weak comparison, how it overrides If-Modified-Since, and its second job preventing duplicate creates.
If-Modified-Since: date based conditional requests
How If-Modified-Since produces a 304, why it only applies to GET and HEAD, when servers must ignore it, and why entity tags are preferred where both exist.
Tools for this
- HTTP Headers Checker Free online HTTP headers checker - view HTTP response headers for any URL
- HTTP Request Builder Free online HTTP request builder - build and test HTTP requests with custom headers and body