Vary
A single URL often produces different responses based on Compression, language, or device type. The Vary response header tells caches which request headers influenced the response, so the correct representation gets stored and served.
Usage
A single URL often produces different responses depending on what the client sends. A server compressing with gzip for one client and Brotli for another serves two distinct representations of the same resource. The Vary header records which request headers caused the server to choose a particular representation, giving caches the information needed to store and match the right copy.
When a cache receives a request for a URL with a stored response, the cache compares the listed Vary headers between the new request and the stored one. If the values match, the cached response is valid. If they differ, the cache fetches a fresh response from the origin.
The most common values are Accept-Encoding (for
Compression negotiation) and Accept (for
content negotiation). Dynamic
serving setups serving different HTML to mobile and
desktop clients based on the
User-Agent string add
Vary: User-Agent to signal this variation.
The wildcard value * means every request is considered
unique and effectively disables caching for the
resource. This is rarely appropriate and most caches
treat a Vary: * response as uncacheable.
Note
Google does not use the Vary header to understand
mobile and desktop relationships for search indexing.
The header is a caching mechanism, not a search
signal. Sites using dynamic serving with
Vary: User-Agent still benefit from correct cache
behavior, but the header itself has no effect on how
Google discovers or indexes mobile content. Google
also advises against making m-dot URLs the canonical
version despite mobile-first indexing, recommending
responsive design as the preferred configuration.
Directives
Accept-Encoding
Indicates the response varies by the Accept-Encoding request header. Different Compression algorithms (gzip, br, zstd) produce different response bodies for the same resource. This is the most widely used Vary value.
Accept
Indicates the response varies by the Accept request header. Content negotiation delivers different media types (HTML, JSON, XML) from the same URL based on client preference.
Accept-Language
Indicates the response varies by the Accept-Language request header. Multilingual sites serving localized content from the same URL include this value.
User-Agent
Indicates the response varies by the User-Agent request header. Dynamic serving configurations deliver different HTML to mobile and desktop clients from the same URL.
Cookie
Vary: Cookie means the response differs based on
the Cookie header value. Caches store a
separate variant for each unique Cookie string,
causing significant cache fragmentation since every
authenticated user has a different session cookie.
Restrict Vary: Cookie to responses that genuinely
depend on cookie values (personalized pages,
dashboards) and avoid setting the value on public
static assets.
* (wildcard)
The wildcard value signals the response varies by factors not captured in a request header. Caches treat this as uncacheable. Rarely used intentionally.
Example
A server compresses responses using the encoding
requested by the client. The Vary: Accept-Encoding
header tells intermediate caches to store separate
copies for gzip and Brotli requests rather than serving
a gzip-compressed response to a Brotli-capable client.
Vary: Accept-Encoding
A content negotiation setup delivers JSON or HTML from the same endpoint. The cache stores separate responses based on the Accept header value.
Vary: Accept
Multiple headers influencing the response are listed as a comma-separated value. Here the response depends on both the accepted encoding and the preferred language.
Vary: Accept-Encoding, Accept-Language
A dynamic serving configuration delivers different HTML to mobile and desktop user agents. The cache stores separate versions keyed by the full User-Agent string.
Vary: User-Agent
Pairing Vary with Cache-Control controls both what varies and how long each variant stays fresh.
Cache-Control: public, max-age=3600
Vary: Accept-Encoding
Troubleshooting
Caching problems caused by a missing or overly broad Vary header often surface as users receiving wrong content.
CDN serving the wrong cached variant. A server returns different content based on a request header but omits the matching Vary value. The CDN caches the first response and serves the same copy to all clients regardless of their request headers. Add the relevant header name to the Vary list. Test by sending requests with different header values through curl and comparing the
X-CacheorAgeresponse headers:curl -H "Accept-Encoding: gzip" -v https://cdn.example.re/style.csscurl -H "Accept-Encoding: br" -v https://cdn.example.re/style.cssCache fragmentation from
Vary: User-Agent. The User-Agent string has thousands of unique values. Listing User-Agent in the Vary header creates a separate cache entry for nearly every visitor, destroying cache hit rates. CDNs like Cloudflare and Fastly normalize the User-Agent into device classes. Where possible, use a normalized header such asSec-CH-UA-Mobileor a CDN-specific device-type header instead of the raw User-Agent.Vary: *disabling caching entirely. The wildcard tells caches that the response varies by factors outside any request header. Most caches treat this as uncacheable and never serve a stored copy. RemoveVary: *unless the intention is to disable all caching for the resource. If a specific request header drives the variation, name the header explicitly.Missing
Vary: Origincausing CORS cache poisoning. A server returns a dynamic Access-Control-Allow-Origin value based on the Origin request header but does not includeVary: Origin. A CDN caches the response with one origin and serves the same CORS header to a different origin, causing browsers to block the response. AddVary: Originto every response that reflects a dynamic origin value. To diagnose, send two curl requests from different origins and check whether the cached Access-Control-Allow-Origin changes:curl -H "Origin: https://a.example.re" -v https://api.example.re/datacurl -H "Origin: https://b.example.re" -v https://api.example.re/dataVary: Accept-Encodinginconsistencies between gzip and Brotli. Some origin servers compress with gzip but a CDN edge re-compresses to Brotli, causing a mismatch between the cached encoding and the Vary key. Ensure the origin and the CDN agree on encoding behavior. In nginx, setgzip_vary on;to addVary: Accept-Encodingautomatically. When using Brotli alongside gzip, confirm both modules add the same Vary value so the cache stores separate entries per encoding.
See also
- RFC 9110: HTTP Semantics - Vary
- Cache-Control
- Accept-Encoding
- Accept
- Content Negotiation
- Caching
- HTTP headers