The reply header, Vary, has been called “the ugliest part of HTTP that we haven't yet improved.” The identical article describes it as a “horrible, kludgy mechanism” alongside “pretty dreadful interoperability” throughout intermediaries. That is normally anywhere sensible engineers rear distant gradually alongside their hands raised.
That’s not exactly an endorsement of Vary, but ugly doesn’t average useless.
One URL can have additional than one accurate response. A server might, for example, provision distinct depiction formats to distinct browsers. If a cache ignores Vary, it risks serving the incorrect bytes to a request. But if it treats all raw header value as distinct, a fistful of akin requests can dispersed into thousands of barely reusable cache entries. Vary tells a cache which petition sectors may power the response, but it does not inform the cache which differences actually matter.
Vary assistance is now accessible in Cache Rules on all plan. The base motionless names the petition headers that may power a response, but you decide how Cloudflare handles all one. You can normalize known negotiation headers, continue exact values through whenever those small differences matter, or bypass cache whenever the assortment is too unpredictable. The base declares what may vary, and you decide how much assortment is really meaningful for the cache.
How Vary works
Vary is a standard HTTP reply header that tells intermediary caches (like Cloudflare) which petition sectors may power the reply sent by the origin. Sites use Vary to assist distinct languages, depiction formats, compression schemes, or local satisfied from the identical URL.
Take one URL that produces two valid representations. A browser requests a webpage:
GET /catalog HTTP/1.1 Host: example.com Accept: text/html
The base returns HTML and identifies Accept as a site that may power the response:
HTTP/1.1 200 OK Content-Type: text/html Cache-Control: public, max-age=3600 Vary: Accept
An API client can petition the identical URL alongside a distinct preference:
GET /catalog HTTP/1.1 Host: example.com Accept: application/json
This time, the accurate reply is JSON. The Vary: Accept header tells the cache that the URL solitary is not adequate to choose between responses. The request’s Accept value must additionally be considered.
Without Vary, whichever reply enters the cache archetypal can be served to the two clients. If HTML wins, the API client receives markup and its JSON parser fails. If JSON wins, a browser expecting a web leaf receives an API response.
Vary prevents the cache from serving the incorrect reply to the requesting client. But it introduces a harder question: whenever two requests merge distinct header values, do they really need distinct responses?
When accurate caching becomes useless
Vary can inform a cache which petition sectors may power a response. It does not inform the cache what the reply represents. For example, obtain an base that serves satisfied in lone English, French, and German. A client power send:
Accept-Language: en-US, fr;q=0.8While another client power request:
Accept-Language: fr;q=0.8, en-GBBoth requests favor English here. The origin’s reply may map the two requests to exactly the identical English response. But a cache comparing the raw values cannot safely assume they are equivalent. They have distinct orders and language tags (that the base doesn’t differentiate). So the cache may shop them as distinct variants, equal whenever their reply bodies merge identical bytes.
This is Vary’s chief problem. Applications frequently create a small, finite set of representations from an enormous set of imaginable petition values. The base understands that thousands of tongue preferences downfall into three supported languages, during a cache normally does not.
This issue compounds whenever a reply varies on multiple fields. Ten imaginable values throughout one site create ten variants. Ten values throughout three sectors can create 1,000 combinations. Real headers can have far greater cardinality: User-Agent values are numerous, cookies can be distinctive to idiosyncratic visitors, and penchant headers can differ in ordering, formatting (spaces and tabs matter!), and quality values.
The outcome is a cache that can be absolutely accurate and nearly permanently chilly (an admission never reused). Identical responses can be scattered throughout entries that obtain too small traffic to remain hot and in cache. They can consume capacity, evict one another, decrease cache hit ratios, and dispatch additional requests rear to base servers. Eviction can eliminate chilly entries, but it cannot merge them fair since the responses are identical.
An analysis of additional than 120 myriad responses from nearly 50,000 famous sites established nearly 3,000 sites varying on four or additional fields. Some varied on 10, 23, or equal 47 fields. We desire to create certain that customers have the tools they need to use Vary whenever appropriate, but not so much that they create a useless cache.
Some high-cardinality assortment is deliberate. CDNs or reverse proxies may inject values, specified as a geographic region, to partition satisfied predictably. That plant whenever the imaginable values are controlled and all component agrees on their meaning. Without those constraints, the cache fragments into variants it may never reuse.
That was the scheme issue we needed to resolve to assistance Vary. We needed to maintain adequate assortment to assist the correct response, without allowing secondary differences between requests to demolish cache efficiency.
How Cache Rules authority Vary
Cloudflare customers already had multiple ways to grip negotiated satisfied akin to Vary. They could bypass cache and let their base agreement alongside it, reproduce the origin's negotiation logic in a custom cache key or another rule, use a Worker, or use features akin Vary for images.
Those options remain useful, but they either provision up caching, copy use logic, need to compose additional code, or location a narrower use case. Vary in Cache Rules may inhabit the gap between these existing features by splitting assistance into two decisions:
- The base uses Vary to acknowledge the petition headers that may power a response.
- The Cache Rule determines how Cloudflare handles the value of all header.
A Cache Rule does not power all reply to vary. If the base does not come back Vary, Cloudflare caches the reply normally, although the regulation may motionless rewrite Accept and Accept-Language before forwarding the petition to the origin.
When the base does come back Vary, Cloudflare uses the configured act for all header it names. Headers without an idiosyncratic environment use the rule’s default action. The three accessible actions are:
Action | What Cloudflare does | Best used for |
normalize | Normalizes petition headers before selecting a cached variant, assisting equal requests portion a cached response. Applies header-specific rules to Accept, Accept-Language, and Accept-Encoding. For another headers, it trims optional whitespace and combines repeated header lines in their first order, preserving casing and inner whitespace. | The recommended starting item for negotiation headers anywhere many petition values map to a small set of responses. |
passthrough | Uses the petition header’s raw bytes for cache matching, preserving casing, whitespace, order, and copy values. If the header appears on multiple lines, Cloudflare combines those lines in command using commas for cache matching. Passthrough leaves the outgoing header lines unchanged. Cloudflare can motionless rewrite Accept-Encoding whenever Respect Strong ETags is disabled. | Headers alongside a controlled set of values, anywhere the exact value changes the response. |
bypass | Does not shop the reply whenever the base 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 specified as Cookie or User-Agent. |
We propose normalize as the default. For idiosyncratic headers alongside individual or unbounded values, use bypass. Use passthrough whenever the exact value changes the response.
For example, passthrough preserves distinctions in casing, whitespace, ordering, and copy values, equal whenever the base treats them as equivalent. With Vary: X-View and passthrough, these three values create distinct cache keys:
X-View: compact,full
X-View: Compact,full
X-View: compact, full
Enough secondary assortment can rotate a reusable reply into many one-off variants in your cache.
Regardless of the configured actions, Vary: * continually bypasses cache. It method any aspect of the request, equal data exterior the HTTP communication (like the client’s IP address), may power which reply the base selects. Cloudflare hence cannot reuse the reply for a afterward petition without contacting the origin.
How a reply moves through cache
Let’s prosecute among the /catalog requests from complete through Cloudflare.
On the archetypal request, Cloudflare has no stored Vary data for the resource, so the cache lookup misses. The matching Cache Rule can normalize configured sectors before Cloudflare contacts the origin.
This can happen before Cloudflare knows whether the eventual reply volition merge Vary. The Cache Rule defines the permitted normalization; the reply afterward determines whether those sectors rotate into part of the cached variant.
That ordering matters. If Cloudflare grouped multiple raw values under one normalized cache key, but the base motionless received those raw values, the base could create distinct responses that the cache would afterward regard interchangeable. Forwarding the normalized value keeps base choice aligned alongside cache matching.
The base responds with:
Vary: Accept, Accept-Language
Cloudflare records those header names and stores the reply as a cached variant. The header values, processed according to the Cache Rule, differentiate this type from others for the identical resource.
When another petition for /catalog arrives, Cloudflare starts alongside the resource’s base cache key: mostly the URL affirmative any another configured key fields. It afterward says the stored Vary sectors and applies the Cache Rule to those headers in the new petition to acknowledge the matching cached variant.
Suppose they normalize to:
Accept: text/html
Accept-Language: en,fr
Cloudflare uses those values to appearance up the matching cached type directly. It does not difference the petition against all stored type one by one.
If a matching type exists and is fresh, the petition is a cache hit. If not, Cloudflare sends the petition to the base and may shop the resulting reply as another variant.
The base reply closes the loop. For all header named in Vary, Cloudflare uses the act configured for that header, or the rule’s default act if the header is not listed individually:
- If it does not merge Vary, Cloudflare caches it normally.
- If all named header resolves to normalize or passthrough, Cloudflare can shop the reply as a cached variant.
- If any named site uses bypass, Cloudflare does not shop the response.
- If the reply contains Vary: *, Cloudflare does not shop it.
This places an crucial duty on the origin. Every cacheable reply that can differ according to petition sectors must come back the suitable Vary header consistently, including errors and fallback responses. If one reply omits it, Cloudflare could cache that reply without the variance needed to keep it isolated.
The cache keys in the diagram are conceptual. The afterward petition assumes a caller cached response.
Any purge targeting a cached asset covers all its Vary variants. Existing requirements for purging tradition cache keys motionless apply.
Changing a Vary configuration does not automatically purge existing content. The new guideline may create distinct cache keys: requests can young female and refill under the new keys, during old entries remain until they expire or are purged.
Normalization keeps equal requests together
Remember the requests from complete asking for English and French?
Accept-Language: en-US, fr;q=0.8
Accept-Language: fr;q=0.8, en-GB
Both requests favor English, but passthrough would treat them as distinct variants. If the Cache Rule allows en, fr, and de, normalize reduces the two to en,fr, allowing them to portion a cached response.
To do this, Cloudflare lowercases values in Accept, Accept-Language, and Accept-Encoding, afterward sorts them by quality value, the highest first, alongside alphabetical ordering to interrupt ties. The client’s ordering hence does not power the cache key. After sorting, Cloudflare strips parameters from entries alongside a nonzero norm value. It can additionally endure q=0 (“not acceptable”) whenever shortening tongue tags or filtering to the configured formats and languages. For example, en-US;q=0 can rotate into en. Use passthrough for Accept or Accept-Language if the base needs to see those exclusions.
You can additionally configure the regulation to keep lone stated media types or languages in Accept and Accept-Language. Regional language tags specified as en-US decrease to their basis language, en, unless the complete tag is configured. This lets you align normalization alongside the formats and languages your base really serves.
To keep base choice aligned alongside cache matching, Cloudflare forwards the normalized Accept and Accept-Language values to the origin. It additionally forwards normalized Accept-Encoding values whenever Respect Strong ETags is enabled. Other headers are normalized lone for cache matching.
Configure Vary in Cache Rules
In the Cloudflare dashboard, go to Caching > Cache Rules, create or edit a rule, create the reply eligible for cache, and add the Vary setting. Set the default behavior, afterward add the headers your base is expected to name.
The identical configuration is accessible through the Rulesets API in the http_request_cache_settings phase. The default environment chooses a fallback act for headers your base names in Vary that you have not configured individually.
This example normalizes Accept and Accept-Language to a configured set of formats and languages. The default normalize act additionally applies to another headers named in Vary:
{ "rules": [ { "ref": "vary_negotiated_content", "description": "Cache bounded negotiated representations", "expression": "(http.host eq \"example.com\" and http.request.uri.path eq \"/catalog\")", "action": "set_cache_settings", "action_parameters": { "cache": true, "vary": { "default": { "action": "normalize" }, "headers": { "accept": { "action": "normalize", "media_types": ["text/html", "application/json"] }, "accept-language": { "action": "normalize", "languages": ["en", "fr", "de"] } } } } } ] }
This is a complete petition build for a PUT to the http_request_cache_settings phase entrypoint. A PUT replaces all regulation in that entrypoint. If you already have Cache Rules, contain them in the rules gathering or use the suitable single-rule create or update procedure instead.
If the base serves one depiction for all media category and tongue pair, there are six satisfied combinations. That does not cap the cache at six keys. Preference order, missing headers, and values that normalize to bare can create more. Keep the supported set small and define the rule’s boundaries clearly. After rollout, test the identical URL alongside distinct header values that should normalize to the identical cached variant. Send the test requests from the identical client, verify they come back the expected format and language, and inspect CF-Cache-Status. Look for hits formerly the cache is populated, and examine persistent young female responses or unexpected bypass responses.
For limitations, additional examples, and how to set this in Terraform, see the Vary documentation.
Why not use a tradition cache key?
At this point, an apparent inquiry is, “why not add Accept and Accept-Language to a custom cache key?”
That plant whenever those sectors are continually part of the resource’s identity. But a tradition cache key adds the configured dimensions to all reply covered by the rule, whether the base used them or not.
Vary is response-driven, but cacheable responses under the identical basis key need a accordant set of Vary fields.
Use a tradition cache key whenever a petition asset continually defines the resource. Use Vary whenever the base declares the identical set of petition sectors throughout cacheable responses. Avoid placing the identical header in the two unless the replication is deliberate and tested.
Use Vary in Cache Rules today!
Vary helps resolve an apparent problem: one URL can have additional than one accurate response. But it hands a cache a harder problem, which petition differences really matter? The base knows which responses it can serve. The cache needs to cognize which requests can reuse all response.
Vary in Cache Rules connects those two views. The base identifies the petition sectors that may power a response. You decide whether to normalize values, use passthrough for exact differences, or keep the reply out of cache.
Vary was never too ugly to be useful. But configuring supported formats and languages manually may not lawsuit all application. We’re evaluating whether ideas from the expired Availability Hints draft could decrease that activity by letting origins depict the representations they assist directly.
Vary in Cache Rules is accessible today on Free, Pro, Business, and Enterprise plans through the Cloudflare dashboard, Rulesets API, and Terraform.