The Vary HTTP response header is the cache's "this response varies based on X" annotation. When a cache stores a response, it remembers the URL plus the values of the headers listed in Vary. Subsequent requests with different values for those headers get separate cached entries.
Without Vary, a cache treats responses to the same URL as interchangeable -- which produces the wrong content when the response actually varied based on a request header.
Common Vary values:
Vary: Accept-Encoding-- response compression varies. The cache stores the gzipped variant separately from the uncompressed variant. Required whenever the server conditionally compresses based onAccept-Encoding: gziprequest header.Vary: Accept-Language-- language varies. Servers that pick content language from theAccept-Languageheader should declare this.Vary: Cookie-- response varies by cookie. Aggressive (kills cache hit rate -- every cookie value gets its own entry) but necessary when the response is per-user.Vary: User-Agent-- device-class detection. Common on legacy "mobile site" setups.Vary: Origin-- CORS-relevant; CORS responses change based on the requesting Origin.
The classic bug: a server compresses responses based on Accept-Encoding but doesn't set Vary. A corporate proxy fetches the gzipped response from a Chrome user, caches it, then serves the same gzipped bytes to an old IE6 client that didn't send Accept-Encoding: gzip. The IE6 client receives garbled bytes because it can't decompress. Adding Vary: Accept-Encoding makes the proxy keep separate entries.
Modern alternative: HTTP cache-key extensions (e.g. Cloudflare's "Cache Key" custom rules) let the cache include arbitrary request properties in the cache key without going through the public Vary mechanism -- more granular but vendor-specific.
The BeaverCheck server-response analyzer flags compressed responses missing Vary (the most common real-world Vary-related bug).