Cloudflare — We just shipped support for the ugliest part of HTTP: Vary¶
Summary¶
Cloudflare shipped Vary support in Cache Rules
on every plan (Free/Pro/Business/Enterprise), configurable via the dashboard,
the Rulesets API (http_request_cache_settings phase), and Terraform. Vary
is a standard RFC 9110
HTTP response header by which an origin declares which request header fields
may affect the response for a given URL — the basis of HTTP
content negotiation (serving different
languages, image formats, compression schemes, or regional content from one
URL). The design problem Cloudflare had to solve is that Vary tells a cache
which headers may matter but not which value differences actually matter:
applications collapse an enormous input space (thousands of Accept-Language
strings) into a small output space (three supported languages), and a cache
that keys on raw header bytes fragments identical responses into thousands of
barely-reused variants — the cache-variant
explosion problem. Cloudflare's answer splits the decision in two: the origin
names the varying headers; the customer's Cache Rule decides, per header, how
Cloudflare handles the value via one of three actions — normalize,
passthrough, or bypass.
Key takeaways¶
-
Varyis response-driven content negotiation, and its failure mode is cache fragmentation. One URL can have multiple correct representations (HTML vs JSON on the same/catalogviaAccept; en/fr/de viaAccept-Language). WithoutVary, whichever response fills the cache first is served to everyone — a browser expecting HTML gets a JSON API response, or vice-versa. With naiveVary, keying on raw header bytes multiplies variants: "Ten values across three fields can create 1,000 combinations." (Source: sources/2026-09-22-cloudflare-we-just-shipped-support-for-the-ugliest-part-of-http-vary) -
The core insight: input cardinality ≫ output cardinality. "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." A cache can be perfectly correct and almost permanently cold — identical response bodies scattered across low-traffic entries that evict one another, lower the hit rate, and send more requests to origin. Eviction removes cold entries but cannot merge them just because the bodies are identical.
-
Three per-header actions decide how much variation is meaningful:
normalize(recommended default): canonicalize the request header before variant selection so equivalent requests share one entry. ForAccept/Accept-Language/Accept-Encoding, apply header-specific rules; for other headers, trim optional whitespace and combine repeated lines in original order. Best for negotiation headers where many request values map to a small response set.passthrough: use the header's raw bytes for cache matching, preserving casing, whitespace, ordering, and duplicates. Best for a controlled value set where the exact value changes the response. Incidental differences turn one reusable response into many one-offs (e.g.X-View: compact,fullvsCompact,fullvscompact, full= three keys).-
bypass: do not store the response when the origin names that header inVary. Best for personalized / high-cardinality / unexpected headers likeCookieorUser-Agent. (Existing entries are not purged automatically.) -
Vary: *always bypasses cache — it declares that any aspect of the request (even information outside the HTTP message, like client IP) may affect the response, so Cloudflare cannot reuse a stored response without contacting the origin. -
Normalization is a canonicalization algorithm on quality-valued lists. For
Accept/Accept-Language/Accept-Encoding, Cloudflare lowercases values, sorts by quality value (highest first, alphabetical tie-break) so the client's ordering doesn't affect the cache key, then strips parameters from nonzero-q entries. Regional language tags (en-US) reduce to base language (en) unless the full tag is configured. BothAccept-Language: en-US, fr;q=0.8andfr;q=0.8, en-GBnormalize toen,frand share one cached response. Caveat: normalization can loseq=0("not acceptable") exclusions — usepassthroughif the origin needs to see exclusions. -
Normalized values are forwarded to the origin to keep selection aligned with matching. Cloudflare can normalize
Accept/Accept-Languageat the Cache Rule before it knows whether the response will even carryVary. If it grouped raw values under one normalized key but forwarded the raw values, the origin could produce responses the cache later treats as interchangeable. So Cloudflare forwards the normalizedAccept/Accept-Language(and normalizedAccept-Encodingwhen Respect Strong ETags is enabled). -
Cache lookup is direct, not a scan. On a request, Cloudflare starts from the resource's base cache key (URL + configured key fields), reads the stored
Varyfield names, applies the Cache Rule to those headers in the new request, and "uses those values to look up the matching cached variant directly. It does not compare the request against every stored variant one by one." -
The origin carries a consistency obligation. Every cacheable response that can differ on request fields must return the appropriate
Varyheader consistently, including error and fallback responses. If one response omits it, Cloudflare could cache that response without the variance needed to isolate it. -
Varyvs. custom cache key are different tools. A custom cache key adds a dimension to every response under the rule whether the origin used it or not — use it when a request property always defines the resource.Varyis response-driven — use it when the origin declares the same set of request fields across cacheable responses. Avoid putting the same header in both unless deliberate and tested. -
Config changes don't auto-purge. Changing a
Varyconfiguration produces different cache keys; requests miss and refill under the new keys while old entries linger until they expire or are purged. Any purge targeting a resource covers all itsVaryvariants.
Operational numbers¶
- An analysis of >120M responses from ~50,000 popular sites found almost 3,000 sites varying on four or more fields; some varied on 10, 23, or even 47 fields — the empirical case for fragmentation risk.
- Combinatorial illustration: 10 values on one field → 10 variants; 10 values across three fields → 1,000 variants.
- Example: six content combinations (media-type × language pairs) do not cap the cache at six keys — preference order, missing headers, and values that normalize to empty can create more.
Systems / concepts / patterns extracted¶
- Systems: Cloudflare Cache (the per-POP HTTP
cache whose cache-key/variant model this extends);
Cloudflare rulesets engine (the
http_request_cache_settingsphase hosts thevaryaction-parameter); Smart Tiered Cache and Pingora (the surrounding cache substrate). - Concepts: content negotiation (the HTTP
mechanism
Varyserves); cache-variant explosion (the fragmentation failure mode being controlled); cache hit rate (the metric fragmentation degrades). - Patterns: none minted — the normalize/passthrough/bypass action set is a product-specific control surface, recorded as prose here and on systems/cloudflare-cache.
Caveats¶
- Product-launch post (a shipped feature), but it clears scope decisively: the majority of the body is HTTP cache-key architecture, the input-vs-output cardinality trade-off, and a concrete normalization algorithm — not marketing.
normalizecan dropq=0exclusions and collapse regional tags; the post explicitly flagspassthroughas the escape hatch when the origin needs exact values.- Cloudflare notes it is evaluating whether ideas from the expired Availability Hints draft could let origins describe representations directly and reduce manual config — forward-looking, not shipped.
Source¶
- Original: https://blog.cloudflare.com/vary-support/
- Raw markdown:
raw/cloudflare/2026-09-22-we-just-shipped-support-for-the-ugliest-part-of-http-vary-f665e9d5.md
Related¶
- companies/cloudflare
- systems/cloudflare-cache
- systems/cloudflare-rulesets-engine
- concepts/content-negotiation
- concepts/cache-variant-explosion
- concepts/cache-hit-rate
- sources/2026-07-06-cloudflare-workers-cache — sibling Cloudflare post where
Varysupport ships for the Worker-owned cache (no allowlist restriction). - sources/2026-04-21-vercel-making-agent-friendly-pages-with-content-negotiation
— content negotiation applied to markdown-for-agents via
Acceptrewriting.