Skip to content

CLOUDFLARE 2026-09-22 Tier 1

Read original ↗

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

  1. Vary is response-driven content negotiation, and its failure mode is cache fragmentation. One URL can have multiple correct representations (HTML vs JSON on the same /catalog via Accept; en/fr/de via Accept-Language). Without Vary, whichever response fills the cache first is served to everyone — a browser expecting HTML gets a JSON API response, or vice-versa. With naive Vary, 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)

  2. 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.

  3. Three per-header actions decide how much variation is meaningful:

  4. normalize (recommended default): canonicalize the request header before variant selection so equivalent requests share one entry. For Accept / 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.
  5. 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,full vs Compact,full vs compact, full = three keys).
  6. bypass: do not store the response when the origin names that header in Vary. Best for personalized / high-cardinality / unexpected headers like Cookie or User-Agent. (Existing entries are not purged automatically.)

  7. 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.

  8. 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. Both Accept-Language: en-US, fr;q=0.8 and fr;q=0.8, en-GB normalize to en,fr and share one cached response. Caveat: normalization can lose q=0 ("not acceptable") exclusions — use passthrough if the origin needs to see exclusions.

  9. Normalized values are forwarded to the origin to keep selection aligned with matching. Cloudflare can normalize Accept / Accept-Language at the Cache Rule before it knows whether the response will even carry Vary. 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 normalized Accept / Accept-Language (and normalized Accept-Encoding when Respect Strong ETags is enabled).

  10. 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 Vary field 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."

  11. The origin carries a consistency obligation. Every cacheable response that can differ on request fields must return the appropriate Vary header consistently, including error and fallback responses. If one response omits it, Cloudflare could cache that response without the variance needed to isolate it.

  12. Vary vs. 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. Vary is 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.

  13. Config changes don't auto-purge. Changing a Vary configuration 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 its Vary variants.

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

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.
  • normalize can drop q=0 exclusions and collapse regional tags; the post explicitly flags passthrough as 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

Last updated · 766 distilled / 2,225 read