Learn how Cloudflare interprets Cache-Control headers set at your origin server.
Cache-Control headers are one way for web administrators to tell Cloudflare how to handle content from the origin. This article explains how Cloudflare makes caching decisions during the request and response phase for a resource, and the options you have for setting Cache-Control directives at your origin server.
In the request phase, the client request URL is matched against a list of cacheable file extensions. If the request matches an extension on this list, Cloudflare serves the resource from cache if present. If the content is stale in the Cloudflare cache, Cloudflare attempts to revalidate the content with the origin before serving the response to the client.
It is possible to modify what Cloudflare deems cacheable via the Cache Level setting in the Cloudflare Page Rule app.
As an example, Cache Everything skips the extension check, and all content is treated as cacheable. Refer to Cloudflare's guide on how to enable Cache Everything.
If Cloudflare deems a request cacheable, we first examine our caches in multiple network locations for content. If the resource is not present in cache, Cloudflare requests the resource from your origin web server to fill our cache. The response is then sent to the client who initiated the request.
At this point, our cache logic examines the HTTP response received from your origin server.
Based on how Cloudflare interprets request headers, the response is either deemed cacheable and written to disk (for use with the next request for the same resource) or deemed un-cacheable (resulting in the next request missing cache and repeating this flow).
Set cache-control directives
The Cache-Control header can include a number of directives. If multiple directives are passed together, each is separated by a comma.
If the directive takes an argument, it follows the directive separated by an equal sign. For example:
These directives break down into groupings as follows:
Cacheability - should this resource enter a cache?
public— Indicates that any cache may store the response, even if the response is normally non-cacheable or cacheable only within a private cache.
private— Indicates that the response message is intended for a single user (eg. a browser cache) and must not be stored by a shared cache (like Cloudflare, or a corporate proxy).
no-store— Indicates that any cache (ie a client or proxy cache) must not store any part of either the immediate request or response.
Expiration - how long should this resource stay in the cache?
max-age=seconds— Indicates that the response is to be considered stale after its age is greater than the specified number of seconds. Age is defined as the time in seconds since the asset was served from the origin server. The seconds argument is an unquoted integer.
s-maxage=seconds— Indicates that, in shared caches, the maximum age specified by this directive overrides the maximum age specified by either the max-age directive or the Expires header field. The s-maxage directive also implies the semantics of the proxy-revalidate response directive. Browsers ignore s-maxage.
no-cache— Indicates that the response cannot be used to satisfy a subsequent request without successful validation on the origin server. This allows an origin server to prevent a cache from using it to satisfy a request without contacting it, even by caches that have been configured to send stale responses.
Revalidation - how should the cache behave when a resource has expired?
must-revalidate— Indicates that once it has become stale, a cache (client or proxy) must not use the response to satisfy subsequent requests without successful validation on the origin server.
proxy-revalidate— Has the same meaning as the must-revalidate response directive, except that it does not apply to private client caches.
stale-while-revalidate=seconds— When present in an HTTP response, indicates that caches may serve the response in which it appears after it becomes stale, up to the indicated number of seconds since the resource expired.
stale-if-error=seconds— Indicates that when an error is encountered, a cached stale response may be used to satisfy the request, regardless of other freshness information.
no-transform— Indicates that an intermediary (regardless of whether it implements a cache) must not transform the payload
vary— Cloudflare doesn't consider vary values when making caching decisions.
immutable— Indicates to clients that the response body does not change over time. The resource, if unexpired, is unchanged on the server and therefore the client should not send a conditional revalidation for it (e.g. If-None-Match or If-Modified-Since) to check for updates, even when the user explicitly refreshes the page. This directive has no effect on public caches like Cloudflare, but does change browser behavior.
Origin Cache-Control Behavior
The table below lists directives and its behaviors when Origin Cache-Control is disabled and when it’s enabled.
|Directive||Origin Cache Control disabled behavior||Origin Cache Control enabled behavior|
|s-maxage=0||Won’t cache||Caches and always revalidates|
|max-age=0||Won't cache||Caches and always revalidates.|
|no-cache;||Won't cache||Caches and always revalidates. Doesn't serve stale.|
|no-cache=#headers||Won't cache at all||Caches if headers mentioned in no-cache=#headers don't exist. Always revalidates if any header mentioned in no-cache=#headers is present.|
|Private=#headers||Won't cache at all||Doesn't cache #headers values mentioned in Private=#headers directive.|
|must-revalidate||Service is stale. Always online.||Doesn't serve stale.|
|proxy-revalidate||Service is stale. Always online.||Must revalidate for CDN but not browser.|
|no-transform||May (un)gzip, polish, email filter, etc.||Doesn't transform body.|
|s-maxage=delta, delta>1||Same as max-age||Max-age and proxy-revalidate|
|immutable||Not proxied downstream||Proxied downstream. Browser facing, doesn't impact caching proxies.|
Additional changes occur when Origin Cache-Control is disabled or enabled.
|Condition||Origin Cache Control disabled behavior||Origin Cache Control enabled behavior|
|Presence of Authorization header||Content may be cached||Content is cached only if must-revalidate, public, or s-maxage is also present|
|Use of no-cache header||In logs, cacheStatus='miss'||In logs, cacheStatus='bypass'|
|Origin response has "Set-Cookie" header, and default cache level is used||Content may be cached with stripped set-cookie header||Content is not cached|
|Browser Cache TTL is set||Cache-Control returned to eyeball does not include 'private'||If origin returns 'private' in cache-control then preserve it.|
For more information about Cache-Control directives at origin servers, refer to the Mozilla Cache-Control documentation
Cache-Control: public, max-age=86400
Cache-Control: private, max-age=3600
Cache-Control: public, no-cache
Cache-Control: public, no-cache, proxy-revalidate or Cache-Control: public, s-maxage=0
Cache-Control: public, no-cache, must-revalidate
Cache-Control: public, no-transform
Cache-Control: public, max-age=3600, stale-if-error=60
With this configuration, Cloudflare attempts to revalidate the content with the origin server after it has been in cache for 3600 seconds (1 hour). If the server is returning an error instead of proper revalidation responses, Cloudflare continues serving the stale resource for a total of 1 minute beyond the expiration of the resource.
Cache-Control: public, max-age=7200, s-maxage=3600
Cache-Control: max-age=600, stale-while-revalidate=30
This configuration indicates the asset is fresh for 600 seconds, and can be served stale for up to an additional 30 seconds to parallel requests for the same resource while the initial synchronous revalidation is attempted.
Interaction with other Cloudflare features
Edge Cache TTL
Edge Cache TTL Page Rules override s-maxage and disable revalidation directives if present. When origin cache control is enabled at Cloudflare, the original Cache-Control header is passed downstream from our edge even if Edge Cache TTL overrides are present. Otherwise, when origin cache control is disabled at Cloudflare (the default), Cloudflare overrides the origin cache control.
Browser Cache TTL
Browser Cache TTL Page Rules override max-age settings passed downstream from our edge, typically to your visitor's browsers.
Polish is disabled when the no-transform directive is present.
Gzip and Other Compression
Compression is disabled when the no-transform directive is present. If the original asset fetched from the origin is compressed, it is served compressed to the visitor. If the original asset is uncompressed, compression is not applied.