What this guide answers
- What
Varymeans for an HTTP response. - Why an origin response header alone does not prove how a CDN builds its cache key.
- How to check a public URL safely, and what the response headers can and cannot tell you.
Short answer: Vary tells a cache which request headers may have affected a response. A CDN’s actual cache key and caching rules still depend on that provider’s configuration. Seeing Vary: Accept-Encoding, Age, or a cache-status header in one response is not enough to count stored variants or prove that every edge behaves the same way.
This article used to present a one-time, single-location curl check of 11 public sites as evidence about CDN behavior. That check was not a controlled multi-region experiment, and the underlying response captures were not available for independent reproduction. The site-by-site measurements and conclusions have therefore been removed. This guide focuses on the standards, current provider documentation, and a repeatable way to test a CDN you operate.
What Vary does
Suppose /catalog returns HTML when a request accepts HTML and JSON when it accepts JSON. The URL is the same, but the representations differ. A response such as Vary: Accept tells a compliant cache to match a stored response against the nominated request header fields before reusing it. That protects a cache from serving the wrong representation.
RFC 9111 section 4.1 specifies the cache matching rules. Header names are case-insensitive; caches can normalize some equivalent field values. Vary: * never matches a later request for reuse. The MDN reference gives a readable overview and notes that the server should send consistent Vary information for a URL, including revalidation responses.
Three layers that are easy to confuse
- Origin behavior: which request fields the application uses to choose a response, and which fields it declares in
Vary. - CDN cache policy: which request fields the provider includes, normalizes, ignores, or uses to bypass its cache.
- Observed response: headers returned by a particular request, through a particular network path and cache layer at a particular time.
Those layers need to agree. If the application varies content by language but the shared cache ignores language, visitors could receive the wrong language. If the cache key includes many values that do not change the representation, the cache may hold redundant variants and get fewer hits.
Provider rules differ
Cloudflare: its current Vary documentation describes three Cache Rules actions: normalize a configured header before matching, pass its raw value through, or bypass caching when the origin varies on that header. The origin response must include Vary for that header to select a variant; Vary: * bypasses cache. This behavior depends on the rule and configuration, so do not assume an origin’s header alone describes the final edge policy.
Google Cloud CDN: its caching documentation lists which Vary values it can cache. Other values prevent caching unless they are configured as cache-key headers. The documentation also warns that high-cardinality fields such as cookies, authorization, user agent, and referer can harm hit rates or are disallowed as cache-key inputs.
Amazon CloudFront: its cache-key guide explains that the default key includes the distribution domain and URL path. Headers, cookies, and query strings are added through a cache policy. A request field that changes the origin representation belongs in the cache key; a field that does not change it can create unnecessary duplicate objects. An origin request policy can forward values without putting them in the key.
These examples are provider-specific. Check the documentation and deployed policy for the CDN in front of your own hostname; do not transfer a rule from one vendor to another.
A safe, reproducible check
Start with a public, non-personalized URL that you control. Avoid account pages, private APIs, URLs with signed parameters, and requests containing real cookies or authorization tokens. Save the date, region, exact request headers, status, and relevant response headers for each run.
URL='https://example.com/static/app.css'
for encoding in identity gzip br; do
printf '\n--- Accept-Encoding: %s ---\n' "$encoding"
curl --silent --show-error --output /dev/null --dump-header - \
--header "Accept-Encoding: $encoding" "$URL" \
| grep -iE '^(HTTP/|vary:|cache-control:|age:|content-encoding:|etag:|via:|cf-cache-status:|x-cache:)'
done
Repeat the same requests several times, with a pause, and—if the question is geographic—run them from a second approved location. Do not add random query strings unless you are specifically testing query-string key behavior; doing so can force misses and invalidate the comparison.
How to read the results
Varyis the origin’s statement about request fields relevant to the response. It is not a report of the CDN’s complete cache key.Agereports the age of a response as it passes through a cache. Similar values can be consistent with reuse, but do not reveal how many variants exist.CF-Cache-Status,X-Cache, andViaare provider- or intermediary-specific clues. Confirm their meaning in the relevant provider documentation; a request may cross more than one cache.Content-Encodingand a body digest help confirm whether two requests received different representations. For a file you control, compare the downloaded body hashes as well as headers.- A single MISS or HIT is one observation. Repeat it and correlate it with the CDN’s logs, cache-key policy, and origin logs before concluding that a configuration is correct.
Common design choices
| Response variation | Safer starting point | What to verify |
|---|---|---|
Compression selected by Accept-Encoding |
Send Vary: Accept-Encoding when the origin selects compressed or uncompressed representations. |
CDN compression settings, cache policy, representation integrity, and downstream behavior. |
Language selected by Accept-Language |
Use a normalized, limited set of language variants or language-specific URLs. | That origin and cache agree on supported languages and fallback behavior. |
| User-specific session or account response | Do not place a personalized response in a shared cache. Use appropriate private/no-store directives and provider rules. | Test with two authorized test accounts and confirm neither receives the other’s response. Never use real customer data in a public test. |
| Many possible values, such as arbitrary user-agent strings | Avoid creating one shared-cache variant per raw value. Normalize only when equivalent values truly produce equivalent content, or bypass caching. | Variant cardinality, hit rate, and origin load from CDN analytics. |
Checklist before changing production
- List the request fields that can actually change the response body or security-sensitive headers.
- For each field, decide whether it belongs in the cache key, should be normalized, or should bypass shared caching.
- Check origin headers and the deployed CDN policy together. Include fallback, error, and revalidation responses.
- Test two distinct representations and two test identities in staging. Verify body content, not just cache-status labels.
- Inspect cache hit rate and origin request volume after deployment. Keep a rollback path and purge stale entries if the key changes.
Security rule: a cache-key mistake can expose one user’s response to another. If a response contains account data, tokens, or other private information, treat it as uncacheable by shared caches unless an explicitly reviewed design proves otherwise.
Sources and limits
- RFC 9111 §4.1, Calculating Cache Keys with the Vary Header
- MDN: Vary response header
- Cloudflare: Vary
- Google Cloud CDN: Caching overview
- Amazon CloudFront: Understand the cache key
Provider features and deployed policies can change. The examples above explain how to investigate; they do not claim to measure the current behavior of 11 sites or any particular production distribution. Confirm live behavior against your own configuration and logs before changing cache policy.
