Two GET requests for the same target URI can require different cached responses. If an origin selects representation metadata or content from request headers such as Accept-Encoding, a cache keyed only by the URI can return a representation selected for a different request.
HTTP’s Vary response field extends cache selection across nominated request fields. It does not merely document negotiation. For a stored response carrying Vary, those nominated fields constrain whether that response can satisfy a later request without revalidation.
A URI can identify several selectable representations
A request target locates a resource, but response selection can depend on request metadata. Compression is a compact example. An origin might send gzip content when the request accepts gzip and an uncompressed representation otherwise.
The two responses can share the same target URI:
GET /assets/app.js HTTP/1.1
Accept-Encoding: gzipand:
GET /assets/app.js HTTP/1.1
Accept-Encoding: identityIf the gzip response contains:
Vary: Accept-Encoding
Content-Encoding: gzipthe cache cannot treat every later request for /assets/app.js as equivalent. The stored response is associated with the request field that participated in selection.
This boundary is separate from freshness. A response can remain fresh and still be ineligible for a request whose nominated fields do not match.
Vary changes response matching rather than the resource identifier
It is common to describe Vary as adding headers to a cache key. That model is useful, but the protocol rule is more precise: when a stored response contains Vary, a cache must compare the nominated request fields from the presented request with those from the request that produced the stored response before using it without revalidation.
The resource URI has not changed. Vary changes the conditions under which a particular stored response matches a later request.
For example:
/assets/app.js
response A <- Accept-Encoding: gzip
response B <- Accept-Encoding: br
response C <- Accept-Encoding absentThese can coexist as stored responses associated with one target URI. Selection first finds stored responses for the target and then applies the Vary constraints.
An absent nominated field is significant. If Accept-Encoding was absent from the original request, it matches a later request for this purpose only when that field is also absent, subject to the matching rules defined by HTTP caching.
Matching is semantic within defined normalization limits
Vary does not require a byte-for-byte comparison of raw header lines. HTTP caching permits transformations that preserve field semantics, including allowed whitespace normalization, combining repeated field lines, and field-specific normalization known to preserve meaning.
That qualification matters for fields whose syntax permits equivalent serializations. A cache can compare normalized values when the field specification establishes the relevant equivalence.
The cache cannot invent equivalence for arbitrary values. If field semantics do not define a safe normalization, treating distinct values as interchangeable can collapse response variants that the origin intended to keep separate.
This also means that a simplistic cache implementation based on concatenating raw request header bytes can be more restrictive than the protocol requires. It might create separate stored variants for requests that are semantically equivalent, reducing reuse without necessarily returning incorrect content.
Vary star makes a stored response fail selection
Vary: * is a special case. The asterisk states that response selection depends on aspects of the request beyond the fields that can be named in Vary.
For cache matching, a stored response with Vary: * always fails to match a later request. A cache therefore cannot select that stored response through ordinary Vary matching.
This is stronger than listing a large set of headers. A list says which request fields participate in selection. The asterisk says that the relevant selection inputs cannot be represented by such a list.
The distinction matters when reasoning about cacheability. Storage and later reuse are separate decisions. A response might be stored under applicable rules yet fail ordinary selection for subsequent requests because its Vary value prevents a match.
Missing Vary can produce cross-request representation reuse
The most consequential failure is not excessive variant count but missing variation metadata.
Suppose an origin compresses a response when Accept-Encoding: gzip is present but omits Vary: Accept-Encoding. A shared cache can store the compressed response as the reusable response for the target URI. A later request with different encoding capabilities can then be matched against that stored response without the selection constraint the origin actually used.
The cache is not able to infer every hidden input to origin selection. The response metadata has to communicate the relevant request-field dimensions.
The same principle applies to other forms of content negotiation. If response selection materially depends on Accept-Language, for example, omitting that field from Vary can let a representation selected for one language preference participate in reuse for another.
Correct Vary metadata aligns the cache’s matching model with the origin’s declared selection dimensions. It does not guarantee that the origin’s selection policy itself is correct.
Broad variation reduces reuse and increases stored variants
Adding a field to Vary partitions requests along another dimension. That can reduce incorrect reuse, but it can also reduce the fraction of requests that match any single stored response.
A high-cardinality request field can create many distinct combinations. If an origin emits Vary over fields whose values differ frequently, the cache can accumulate variants with little reuse among them, subject to its storage and eviction policies.
This is not a reason to omit a field that genuinely affects representation selection. Doing so trades storage efficiency for incorrect matching semantics. The engineering question is whether the origin truly needs that request dimension to select the response.
Fields controlled by intermediaries deserve the same analysis. A cache can only apply matching to the request metadata visible at its own boundary. If an upstream component transforms or removes a nominated field, the effective variant space at each cache layer can differ.
Vary and validators solve different cache decisions
A validator such as an entity tag answers whether a stored representation still corresponds to the server’s current selected representation during a conditional request. Vary answers whether that stored response is a candidate for the presented request in the first place.
The order is important. A cache should not take a validator from a representation selected for an incompatible request variant and treat it as the validator for another variant merely because the URI matches.
When no stored response satisfies the Vary matching rules, the cache cannot use that response to satisfy the presented request. It will commonly forward the request, potentially using applicable preconditions for stored responses as defined by HTTP caching rules.
Freshness, variant matching, and validation therefore form separate constraints. A response can be fresh but not match the request variant; it can match the variant but be stale; and a stale matching response can sometimes be revalidated without transferring its representation again.
Vary occupies the selection boundary. It records which visible request fields distinguish reusable responses for one target, making cache reuse conditional on the same dimensions that the origin declares as relevant to response selection.