Skip to content
INTERNET INFRASTRUCTURE & NETWORKS

Cloudflare Brings Granular Control to HTTP Vary with New Cache Rules Integration

The HTTP response header known as Vary has long held a notorious reputation across the web development and engineering community. Often labeled by performance experts and specification authors as the ugliest, most convoluted mechanism in the entire HTTP specification, Vary has historically struggled with abysmal interoperability across the myriad intermediaries, proxies, and content delivery networks that populate the modern web. When faced with its messy implementation details, pragmatic engineers have traditionally backed away slowly with their hands raised, preferring to bypass caching altogether rather than wrangle with its complexities.

Yet, in web architecture, being ugly does not automatically render a feature useless. The underlying challenge that Vary attempts to solve is both fundamental and unavoidable: a single uniform resource locator (URL) can frequently correspond to multiple correct, valid responses depending on the context of the incoming request. A web server might, for example, deliver an image in modern formats like AVIF or WebP to a browser that supports them, while falling back to a legacy JPEG format for an older client requesting the exact same URI. Similarly, an application might serve HTML content to a standard web browser, but switch to JSON when an API client makes a call to the exact same endpoint.

If a caching layer completely ignores the Vary header, it risks serving entirely incorrect bytes to an unsuspecting client, breaking parsers and rendering broken interfaces. However, if a cache treats every raw, unnormalized value of a Vary header as entirely distinct, a mere handful of slightly varying user requests can balloon into thousands of fragmented cache entries, virtually destroying cache reusability. Ultimately, while the traditional Vary header instructs a cache on which request fields may influence a response, it completely fails to tell the cache which specific differences actually matter to the application logic.

To address this persistent industry challenge, Cloudflare has announced the broad availability of advanced Vary support within its Cache Rules feature set across every service tier, from free plans up to enterprise configurations. While the origin server retains the responsibility of declaring which request headers may affect a response, administrators are now empowered to dictate precisely how Cloudflare handles each individual header. Teams can choose to normalize known content negotiation headers, pass exact raw values through when minute differences are critical to application behavior, or gracefully bypass the cache entirely when the variation proves too unpredictable to handle efficiently. The origin declares what may vary, but engineers finally decide how much of that variation is truly meaningful for cache performance.

Understanding How Vary Works in HTTP

To grasp the significance of this new capability, it helps to examine the mechanics of the standard HTTP response header. Defined formally in RFC 9110, Vary is a mechanism utilized by origin servers to communicate to intermediary caches—such as Cloudflare’s edge network—which specific request fields could potentially alter the response generated by the origin. Websites rely on Vary to correctly serve localized languages, optimized image formats, various compression schemes, or regional content variations from a single, unified URL.

Consider a scenario where a single endpoint produces two distinct yet entirely valid representations of a resource. A standard web browser sends a request for a catalog page:

GET /catalog HTTP/1.1
Host: example.com
Accept: text/html

The origin server processes this request, returns standard HTML markup, and includes a response header identifying the Accept field as a critical factor that influenced the output:

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: public, max-age=3600
Vary: Accept

Shortly after, an API client requests the exact same URL, but expresses a preference for structured data:

GET /catalog HTTP/1.1
Host: example.com
Accept: application/json

In this second instance, the correct response from the server is a JSON payload rather than HTML markup. The Vary: Accept header serves as an explicit warning to any intermediary cache that the URL string alone is insufficient for choosing the correct response out of storage. The incoming request’s Accept header value must also be factored into the equation.

Without Vary properly functioning, whichever response happens to enter the cache first can be indiscriminately served to subsequent clients regardless of their actual requirements. If the HTML response wins the race and populates the cache, the API client receives raw markup instead of JSON data, causing its parser to fail catastrophically. Conversely, if the JSON response enters the cache first, an end user browsing the site in a web browser might be greeted by a wall of raw JSON data instead of a rendered web page.

While Vary successfully prevents caches from serving entirely incorrect data to requesting clients, it introduces a far more insidious architectural dilemma: when two distinct requests contain slightly different header values, do they genuinely require entirely separate responses from the origin?

When Correct Caching Becomes Inefficient

The fundamental limitation of the traditional Vary mechanism is that it tells an intermediary cache which request fields may affect a response, but it provides zero insight into what the response actually represents or how the application processes those fields.

Take, for example, an origin server that is configured to serve content in just three languages: English, French, and German. Two different users might visit the site with slightly different browser configurations. The first user sends an Accept-Language header structured like this:

Accept-Language: en-US, fr;q=0.8

Meanwhile, a second user requests the page with a slightly reordered preference:

Accept-Language: fr;q=0.8, en-GB

Strictly speaking, both of these incoming requests ultimately prioritize English content. The origin server’s internal language negotiation logic will map both requests to the exact same underlying English response body. However, a conventional caching layer that evaluates raw header values byte-for-byte cannot safely assume these two requests are equivalent. Because they feature different ordering schemes and subtle variations in regional language tags—details that the origin server’s specific implementation might completely ignore—the cache treats them as entirely separate variants. Consequently, the cache stores them as distinct entries, even though their response bodies contain identical bytes.

This highlights the core architectural problem associated with Vary. Modern web applications frequently produce a small, finite, and well-defined set of output representations derived from an enormous, practically infinite space of possible request header combinations. The origin server understands that thousands of nuanced user language preferences ultimately collapse down into three cleanly supported languages, whereas an intermediary cache is completely oblivious to this business logic.

This inefficiency compounds exponentially when a response is allowed to vary across multiple fields simultaneously. Ten possible values spread across a single request header create ten distinct cache variants. However, ten possible values across three separate headers can explode into one thousand unique combinations. In real-world web environments, common request headers exhibit vastly higher cardinality: User-Agent strings are extraordinarily numerous and fragmented, cookies can be entirely unique to individual visitors, and content preference headers can differ wildly in ordering, whitespace formatting, and explicit quality values.

The resulting symptom is a caching infrastructure that operates with absolute technical correctness while remaining almost permanently cold, meaning cache entries are rarely, if ever, reused. Identical server responses end up scattered across thousands of disparate cache entries that receive far too little individual traffic to remain warm in memory. These fragmented entries consume valuable edge capacity, trigger constant evictions, severely degrade overall cache hit ratios, and ultimately drive a massive surge in unnecessary requests back to origin servers. While cache eviction algorithms can successfully clear out cold entries to free up storage space, they lack the contextual awareness required to merge entries simply because their underlying response bodies happen to be identical.

Recent industry analysis scanning more than 120 million responses across nearly 50,000 popular websites revealed that almost 3,000 sites configured their content to vary on four or more distinct request fields. A subset of those sites pushed the boundaries even further, varying responses on 10, 23, or even 47 separate fields. Providing engineers with the ability to leverage Vary where appropriate is essential, but doing so without guardrails often leads to severe cache bloat and diminished performance returns.

To be sure, some high-cardinality variations are entirely deliberate. Content delivery networks and reverse proxies routinely inject custom headers—such as a geographic region code—to partition content predictably across edge regions. This approach succeeds when the possible values are strictly controlled and every component in the stack shares a unified understanding of their meaning. Lacking those rigid constraints, however, the cache inevitably fragments into a vast ocean of variants that it may never successfully reuse.

Solving this design challenge required building a system that could preserve enough granular variation to ensure the correct response reaches the client, while preventing incidental, meaningless differences between requests from completely destroying cache efficiency.

Controlling Vary via Cache Rules

Prior to this release, administrators utilizing Cloudflare had access to several distinct mechanisms for handling content negotiation scenarios similar to Vary. Teams could choose to bypass the cache entirely and let their origin servers handle the computational burden, attempt to meticulously reproduce the origin’s internal negotiation logic within a custom cache key configuration, write custom logic inside a Cloudflare Worker, or rely on narrow, dedicated features such as Vary for Images.

While each of these existing options retains its utility, they invariably require compromises: teams must either give up the benefits of edge caching, duplicate complex application logic at the edge, write and maintain additional code, or contend with restricted use cases. The introduction of Vary controls inside Cloudflare Cache Rules aims to bridge this capability gap by decoupling support into distinct, manageable operational decisions.

Crucially, implementing a Cache Rule does not force every single response to automatically utilize Vary. If an origin server declines to return a Vary header in its response, Cloudflare caches the resulting asset normally, though the active rule may still normalize headers like Accept and Accept-Language before forwarding the request onward to the origin.

When an origin server does return a Vary header, Cloudflare evaluates the configured action assigned to each header named in the response. Headers that lack an individual explicit setting automatically fall back to the rule’s designated default action. Three distinct actions are available for administrators to deploy: normalize, passthrough, and bypass.

The normalize action processes request headers before the edge selects a cached variant, ensuring that semantically equivalent requests can successfully share a cached response. It applies specific, intelligent formatting rules to standard headers such as Accept, Accept-Language, and Accept-Encoding. For any other arbitrary header, it trims optional leading and trailing whitespace and combines repeated header lines in their original order while carefully preserving case sensitivity and interior whitespace. This action serves as the recommended starting point for negotiation headers where a wide multitude of incoming request values ultimately map down to a small, predictable set of server responses.

Alternatively, the passthrough action instructs the system to use the exact raw bytes of the request header for cache matching, strictly preserving casing, whitespace, ordering, and duplicate values. If a targeted header appears across multiple lines in the raw HTTP request, Cloudflare combines those lines in order using commas for the purpose of cache matching, while leaving the outgoing header lines completely untouched on their journey to the origin. This setting is best deployed for custom headers with a strictly controlled set of values where minor syntax variations genuinely change the server’s output.

Finally, the bypass action instructs the network to avoid storing the response altogether whenever the origin names that specific header within its Vary response. Existing cache entries are not automatically purged by this action, requiring administrators to trigger manual purges if older, improperly cached variants need to be cleared from the network. This approach is ideally suited for highly personalized, high-cardinality, or unpredictable headers such as Cookie or User-Agent.

For the vast majority of standard deployments, normalization is recommended as the sensible default starting point. For individual headers carrying deeply personal, highly volatile, or unbounded values, bypassing the cache is the safest operational choice, while passthrough should be reserved strictly for cases where exact syntactic differences mandate unique server responses.

To illustrate the danger of ignoring normalization, consider what happens when passthrough is applied to a header like X-View. Passthrough strictly preserves distinctions in casing, whitespace, ordering, and duplicate values, even if the underlying origin application treats them as completely identical. Under a Vary: X-View configuration paired with passthrough, three slightly formatted variations will needlessly generate three entirely separate, unshared cache keys:

X-View: compact,full
X-View: Compact,full
X-View: compact, full

A sufficient accumulation of such incidental, meaningless variations can quickly transform a highly reusable server response into a multitude of isolated, one-off cache variants that consume storage capacity without delivering any performance benefit.

Regardless of any custom actions configured by an administrator, the wildcard directive Vary: * will always result in a complete cache bypass. This directive explicitly signals that any aspect of the incoming request—including contextual metadata existing entirely outside the HTTP message structure, such as the client’s originating IP address—may have influenced the origin’s choice of response. Consequently, Cloudflare cannot safely reuse that response for any subsequent request without directly contacting the origin server.

The Lifecycle of a Cached Response

To understand how these pieces operate in practice, consider the lifecycle of a request moving through the Cloudflare network for the /catalog endpoint discussed earlier.

When the very first request arrives at the edge, Cloudflare possesses no stored Vary metadata for that specific resource, resulting in an initial cache lookup miss. The matching Cache Rule can immediately normalize the configured request fields before Cloudflare ever establishes contact with the origin server.

This normalization step occurs before the edge network knows whether the eventual response returned by the origin will even contain a Vary header. The Cache Rule establishes the permitted boundaries of normalization, while the eventual server response determines whether those specific fields become integrated into the persistent structure of the cached variant.

This sequencing is critical. If Cloudflare were to group several distinct raw header values under a single normalized cache key, but the origin server still received those raw, unnormalized values, the origin could theoretically produce entirely different responses that the cache would subsequently treat as interchangeable. Forwarding the normalized value ensures that the origin’s selection logic remains perfectly aligned with the edge’s cache matching algorithms.

When the origin server finally responds, it returns:

Vary: Accept, Accept-Language

Cloudflare logs those specific header names and stores the server’s response as a dedicated cached variant. The processed header values, handled according to the instructions defined in the active Cache Rule, cleanly distinguish this specific variant from any others stored for the same underlying resource.

When a subsequent request for /catalog arrives at the network, Cloudflare begins its evaluation using the resource’s established base cache key—typically consisting of the URL path combined with any other configured key attributes. It then reads the stored Vary field definitions and applies the Cache Rule to those specific headers within the new incoming request to pinpoint the matching cached variant.

Suppose the incoming request headers normalize to:

Accept: text/html
Accept-Language: en,fr

Cloudflare utilizes those computed values to look up the matching cached variant directly in storage. It avoids the computational overhead of comparing the incoming request against every single stored variant one by one.

If a matching variant exists within the cache and remains fresh according to its expiration rules, the request is recorded as a successful cache hit. If no matching variant is found, Cloudflare forwards the request onward to the origin server and may subsequently store the resulting payload as an additional variant in the cache.

This final step places a significant operational responsibility squarely on the shoulders of the origin application. Every single cacheable response that can potentially differ based on request header values must consistently return the appropriate Vary header, including error pages and fallback responses. If even a single response variant omits the required Vary header, Cloudflare could inadvertently cache that response without the necessary variance metadata required to keep it safely isolated from other users.

Normalization and Cache Sharing

To keep equivalent requests grouped together efficiently, normalization processes target headers like Accept, Accept-Language, and Accept-Encoding. Cloudflare automatically lowercases values found within these headers, sorts them by their explicit quality values—placing higher-priority values first—and applies alphabetical sorting to break any remaining ties. As a direct result, the order in which a client structures its preferences no longer negatively impacts the cache key.

Following this sorting process, normalization strips unnecessary parameters from entries that feature a nonzero quality value. However, administrators should note that this process can discard explicit q=0 directives—which signal that a format is unacceptable—when shortening regional language tags or filtering down to explicitly configured formats and languages. For instance, an incoming preference of en-US;q=0 may be normalized down to en. If an origin application strictly relies on those explicit exclusions, engineers should utilize the passthrough action for Accept or Accept-Language rather than normalization.

Administrators can also configure rules to retain only specified media types or languages within Accept and Accept-Language. Regional language tags such as en-US are automatically reduced to their base language, en, unless the full regional tag is explicitly configured in the rule parameters. This allows teams to align normalization behavior precisely with the exact subset of formats and languages that their origin servers are built to serve.

To ensure that origin selection never drifts out of alignment with edge cache matching, Cloudflare explicitly forwards the normalized Accept and Accept-Language values back to the origin server. It similarly forwards normalized Accept-Encoding values whenever the Respect Strong ETags setting is enabled. Other custom headers are normalized strictly for internal cache matching purposes.

Availability and Implementation

Vary support within Cache Rules is available across Free, Pro, Business, and Enterprise service tiers. Teams can configure these settings directly through the Cloudflare dashboard by navigating to the Caching and Cache Rules section, creating or editing a rule to ensure the response is marked as eligible for caching, and appending the Vary configuration setting. After establishing a default behavior, administrators can list the specific headers their origin servers are expected to utilize.

The exact same configuration architecture is fully supported programmatically via the Rulesets API under the http_request_cache_settings phase, as well as through infrastructure-as-code tooling like Terraform. Because updating these rules via API endpoints typically replaces existing configurations within that phase, teams managing complex rule sets should ensure all active rules are included in their deployment payloads.

When rolling out new Vary configurations, administrators should keep in mind that modifying a rule does not automatically trigger a global purge of existing edge content. Because the updated policy may generate entirely new cache keys, initial requests may result in cache misses and gradually refill under the new keys, while legacy entries remain stored until they naturally reach their expiration time or are explicitly purged.

Testing a new configuration is straightforward: engineers can issue identical requests to a target URL utilizing varying header orderings or syntax styles that should logically normalize to the same cached variant. By sending test requests from a consistent client environment, confirming that the origin returns the expected format and language, and inspecting the CF-Cache-Status response headers, teams can verify that cache hits populate correctly once the edge storage is warmed.

By bridging the gap between origin-driven content variation and edge cache optimization, the integration of Vary into Cloudflare Cache Rules equips developers with the fine-grained control needed to maintain high cache hit ratios without sacrificing the flexibility required by modern web applications.

Leave a Reply

Your email address will not be published. Required fields are marked *