§02 Web Standards
HTTP Content Negotiation: When to Use the Vary Header

Shlama Syriac Aramaic word for peace / SchnitzelMannGreek., CC BY-SA 3.0
The Vary response header is the pivot point in HTTP content negotiation. Negotiated content can only be cached if the server explicitly directs caches which headers influenced the response influenced the response. But it's exactly that direction that makes the system fragile.
As a protocol, HTTP lets a server select among representations for a resource using request headers like Accept-Language and Accept. The Vary header is pivotal: it tells caches, CDNs, and clients which request headers the server considered in generating the response, so they can reuse that response for requests with the same values. https://datatracker.ietf.org/doc/html/rfc9110 https://datatracker.ietf.org/doc/html/rfc9110 https://datatracker.ietf.org/doc/html/rfc9110 https://datatracker.ietf.org/doc/html/rfc9110 https://httpd.apache.org/docs/current/content-negotiation.html https://developers.cloudflare.com/cache/concepts/vary/
When negotiation holds up best is when a server can serve variant representations of a single resource from the same URL. Most often this is for delivering language-localized content or negotiating media types like HTML, JSON and XML.
If a server selects HTML, JSON, or XML based on the Accept request header, responses can be reused by caching intermediaries when the same Accept header is present in subsequent requests.
Here illustration of what the HTTP server can do:
Accept: text/html .. Accept: application/json
``jsp { "some": "data" } `
Accept: application/xml
`xml .. `
The Vary header makes this possible by tagging the response with the headers that were decisive for the server’s choice. For instance, the Vary: Accept header grants reusability across any request carrying the same value of that Accept header.
However, the key challenge with Vary is that it changes the algorithm that caches and CDNs use to select a representation for a client’s request: the cache key becomes a function not just of the URL, but of whichever headers the Vary header lists.
When the server imposes caching on intermediaries via Vary, some immediate consequences kick in:
- Shared caches at CDNs, in browsers, or at hosting providers will now hold multiple copies of a response, indexed on the Vary values.
- Debugging shared links often becomes impossible when Vary
causes a representation to be indexed on an unintended header like the user’s Accept header, cookie or host. - Setting the wrong Vary
selection key, or usingVary: *improperly, will at worst bypass any caching for that resource, or at best cause it to be cached repeatedly as new copies. - Unconfigured, Vary: *
negates any caching, lowering performance and increasing traffic. - Mistakes in Vary
configuration can lead to uncontrolled cache growth as responses multiply for each varied header.
It can be safer and simpler, then, to serve distinct URLs for variant representations of a resource. Instead of serving /data as HTML, JSON and XML via Accept: text/html, Accept: application/json and Accept:application/xml, the server could serve /data.html, /data.json and /data.xml—distinct endpoints for each syntactic representation.
URL separation means users can share a link that reliably delivers the intended representation—no need to test the link or debug an unintended Vary check. A fixed URL wouldn’t be indexed under a Vary value, and wouldn’t split a cache at the whim of server config or negotiator input. Each URL is an isolated cache key.
But Vary remains a crucial part of HTTP’s basic tools. If a server selects representations based on input headers, it dictates on one side that such negotiation is safe, and on the other marks the conditions under which a stale but OK cache response can be reused. Negotiation is flexible where URL separation fixes URLs. It still matters which HTTP standard dictates it: the core IETF specifications or particular implementations. https://developers.cloudflare.com/cache/concepts/vary/ https://www.hjp.at/doc/rfc/rfc2295.html https://www.hjp.at/doc/rfc/rfc2295.html https://developers.cloudflare.com/changelog/post/2026-07-02-vary-for-cache-rules/ https://developers.cloudflare.com/cache/concepts/vary/
Even after all its evolution, Vary remains the core contract between a server negotiating responses on the fly and the caches storing those snapshots for later reuse. So long as HTTP keeps that contract—so long as servers trumpet the Accept` headers distinguishing the deltas between client requests—caching remains sound.