We just shipped support for the ugliest part of HTTP: Vary
Pangram verdict · v3.3
We believe that this text is a mix of AI and human-written content.
AI likelihood · overall
MixedArticle text · 1,337 words · 9 segments analyzed
The response header, Vary, has been called “the ugliest part of HTTP that we haven't yet improved.” The same post describes it as a “horrible, kludgy mechanism” with “pretty abysmal interoperability” across intermediaries. That is usually where sensible engineers back away slowly with their hands raised. That’s not exactly an endorsement of Vary, but ugly doesn’t mean useless. One URL can have more than one correct response.
A server might, for example, deliver different image formats to different browsers. If a cache ignores Vary, it risks serving the wrong bytes to a request. But if it treats every raw header value as distinct, a handful of similar requests can spread into thousands of barely reusable cache entries. Vary tells a cache which request fields may affect the response, but it does not tell the cache which differences actually matter.Vary support is now available in Cache Rules on every plan. The origin still names the request headers that may affect a response, but you decide how Cloudflare handles each one. You can normalize known negotiation headers, pass exact values through when those small differences matter, or bypass cache when the variation is too unpredictable.
The origin declares what may vary, and you decide how much variation is actually meaningful for the cache.How Vary worksVary is a standard HTTP response header that tells intermediary caches (like Cloudflare) which request fields may affect the response sent by the origin.
Sites use Vary to serve different languages, image formats, compression schemes, or regional content from the same URL.Take one URL that produces two valid representations. A browser requests a webpage:GET /catalog HTTP/1.1 Host: example.com Accept: text/htmlThe origin returns HTML and identifies Accept as a field that may affect the response:HTTP/1.1 200 OK Content-Type: text/html Cache-Control: public, max-age=3600 Vary: AcceptAn API client can request the same URL with a different preference:GET /catalog HTTP/1.1 Host: example.com Accept: application/jsonThis time, the correct response is JSON. The Vary: Accept header tells the cache that the URL alone is not enough to choose between responses. The request’s Accept value must also be considered.Without Vary, whichever response enters the cache first can be served to both clients. If HTML wins, the API client receives markup and its JSON parser fails.
If JSON wins, a browser expecting a web page receives an API response.Vary prevents the cache from serving the wrong response to the requesting client. But it introduces a harder question: when two requests contain different header values, do they actually need different responses?When correct caching becomes uselessVary can tell a cache which request fields may affect a response. It does not tell the cache what the response represents. For example, take an origin that serves content in only English, French, and German. A client might send:Accept-Language: en-US, fr;q=0.8While another client might request:Accept-Language: fr;q=0.8, en-GBBoth requests prefer English here. The origin’s response may map both requests to exactly the same English response. But a cache comparing the raw values cannot safely assume they are equivalent. They have different orders and language tags (that the origin doesn’t differentiate).
So the cache may store them as separate variants, even when their response bodies contain identical bytes.This is Vary’s central problem. Applications often produce a small, finite set of representations from an enormous set of possible request values. The origin understands that thousands of language preferences collapse into three supported languages, while a cache usually does not.This problem compounds when a response varies on multiple fields. Ten possible values across one field create ten variants. Ten values across three fields can create 1,000 combinations. Real headers can have far greater cardinality: User-Agent values are numerous, cookies can be unique to individual visitors, and preference headers can differ in ordering, formatting (spaces and tabs matter!), and quality values.The result is a cache that can be perfectly correct and almost permanently cold (an entry never reused). Identical responses can be scattered across entries that receive too little traffic to remain hot and in cache.
They can consume capacity, evict one another, reduce cache hit ratios, and send more requests back to origin servers. Eviction can remove cold entries, but it cannot merge them just because the responses are identical. An analysis of more than 120 million responses from nearly 50,000 popular sites found almost 3,000 sites varying on four or more fields. Some varied on 10, 23, or even 47 fields. We want to make sure that customers have the tools they need to use Vary when appropriate, but not so much that they create a useless cache. Some high-cardinality variation is deliberate. CDNs or reverse proxies may inject values, such as a geographic region, to partition content predictably. That works when the possible values are controlled and every component agrees on their meaning. Without those constraints, the cache fragments into variants it may never reuse.That was the design problem we needed to solve to support Vary. We needed to preserve enough variation to serve the right response, without allowing incidental differences between requests to destroy cache efficiency.How Cache Rules control VaryCloudflare customers already had several ways to handle negotiated content similar to Vary. They could bypass cache and let their origin deal with it, reproduce the origin's negotiation logic in a custom cache key or other rule, use a Worker, or use features like Vary for images. Those options remain useful, but they either give up caching, duplicate application logic, need to write additional code, or address a narrower use case. Vary in Cache Rules may fill the gap between these existing features by splitting support into two decisions: The origin uses Vary to identify the request headers that may affect a response.The Cache Rule determines how Cloudflare handles the value of each header.A Cache Rule does not force every response to vary. If the origin does not return Vary, Cloudflare caches the response normally, though the rule may still rewrite Accept and Accept-Language before forwarding the request to the origin. When the origin does return Vary, Cloudflare uses the configured action for each header it names. Headers without an individual setting use the rule’s default action. The three available actions are:ActionWhat Cloudflare doesBest used fornormalizeNormalizes request headers before selecting a cached variant, helping equivalent requests share a cached response. Applies header-specific rules to Accept, Accept-Language, and Accept-Encoding.
For other headers, it trims optional whitespace and combines repeated header lines in their original order, preserving casing and interior whitespace.The recommended starting point for negotiation headers where many request values map to a small set of responses.passthroughUses the request header’s raw bytes for cache matching, preserving casing, whitespace, order, and duplicate values. If the header appears on multiple lines, Cloudflare combines those lines in order using commas for cache matching. Passthrough leaves the outgoing header lines unchanged. Cloudflare can still rewrite Accept-Encoding when Respect Strong ETags is disabled.Headers with a controlled set of values, where the exact value changes the response.bypassDoes not store the response when the origin names that header in Vary. Existing cache entries are not removed, so purge them if they need to be cleared.Use for personalized, high-cardinality, or unexpected headers such as Cookie or User-Agent.We recommend normalize as the default. For individual headers with personal or unbounded values, use bypass. Use passthrough when the exact value changes the response.For example, passthrough preserves distinctions in casing, whitespace, ordering, and duplicate values, even when the origin treats them as equivalent.
With Vary: X-View and passthrough, these three values produce separate cache keys:X-View: compact,fullX-View: Compact,fullX-View: compact, fullEnough incidental variation can turn a reusable response into many one-off variants in your cache.Regardless of the configured actions, Vary: * always bypasses cache. It means any aspect of the request, even information outside the HTTP message (like the client’s IP address), may affect which response the origin selects. Cloudflare therefore cannot reuse the response for a later request without contacting the origin.How a response moves through cacheLet’s follow one of the /catalog requests from above through Cloudflare.On the first request, Cloudflare has no stored Vary data for the resource, so the cache lookup misses.