Skip to content
Visit Cache on GitHub
Set theme to dark (⇧+D)

Origin Cache Control

Set Cache-Control headers to tell Cloudflare how to handle content from the origin.

When a user sends an HTTP request, the user’s 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 it is present. If the content is stale in Cloudflare’s cache, Cloudflare attempts to revalidate the content with the origin before serving the response to the client.

In the response, Cloudflare first examines its caches in multiple network locations for content. If the resource is not present in the cache, Cloudflare requests the resource from your origin web server to fill the cache. The response is then sent to the client who initiated the request.

At this point, Cloudflare’s 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 the request is deemed un-cacheable which means the next request misses the cache and repeats this flow.

Cache-control directives

A Cache-Control header can include a number of directives, and the directive dictates who can cache a resource along with how long those resources can be cached before they must be updated.

If multiple directives are passed together, each directive is separated by a comma. If the directive takes an argument, it follows the directive separated by an equal sign. For example: max-age=86400.

Directives can be broken down into four groups: cacheability, expiration, revalidation, and other.


Cacheability refers to whether or not a resource should enter a cache, and the directives below indicate a resource’s cacheability.

  • public — Indicates any cache may store the response, even if the response is normally non-cacheable or cacheable only within a private cache.
  • private — Indicates the response message is intended for a single user (e.g. a browser cache) and must not be stored by a shared cache like Cloudflare or a corporate proxy.
  • no-store — Indicates any cache (i.e., a client or proxy cache) must not store any part of either the immediate request or response.


Expiration refers to how long a resource should remain in the cache, and the directives below affect how long a resource stays in the cache.

  • max-age=seconds — Indicates the response is 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 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 the origin to satisfy a request without contacting it, even by caches that have been configured to send stale responses.

Ensure the HTTP Expires header is set in your origin server to use Greenwich Mean Time (GMT) as stipulated in RFC 2616.


Revalidation determines how the cache should behave when a resource expires, and the directives below affect the revalidation behavior.

  • must-revalidate — Indicates that once the resource is 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 caches may serve the response in which it appears after it becomes stale, up to the indicated number of seconds since the resource expired. If Always Online is enabled, then the stale-while-revalidate and stale-if-error directives are ignored. This directive is not supported when using the Cache API methods cache.match or cache.put. For more information, refer to the Worker's documentation for Cache API.
  • 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. This directive is not supported when using the Cache API methods cache.match or cache.put. For more information, refer to the Worker's documentation for Cache API.

The stale-if-error directive is ignored if Always Online is enabled or if an explicit in-protocol directive is passed. Examples of explicit in-protocol directives include a no-store or no-cache cache directive, a must-revalidate cache-response-directive, or an applicable s-maxage or proxy-revalidate cache-response-directive.


Additional directives that influence cache behavior are listed below.

  • no-transform — Indicates that an intermediary — regardless of whether it implements a cache — must not transform the payload
  • vary — Cloudflare does not consider vary values in caching decisions.
  • immutable — Indicates to clients the response body does not change over time. The resource, if unexpired, is unchanged on the server. The user 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 their behaviors when Origin Cache-Control is disabled and when it's enabled.

DirectiveOrigin Cache-control disabled behaviorOrigin Cache-control enabled behavior
s-maxage=0Will not cacheCaches and always revalidates
max-age=0Will not cacheCaches and always revalidates
no-cacheWill not cacheCaches and always revalidates. Does not serve stale.
no-cache=#headersWill not cache at allCaches if headers mentioned in no-cache=#headers do not exist. Always revalidates if any header mentioned in no-cache=#headers is present.
Private=#headersWill not cache at allDoes not cache #headers values mentioned in Private=#headers directive.
must-revalidateCache directive is ignored and stale is served.Does not serve stale. Must revalidate for CDN but not for browser.
proxy-revalidateCache directive is ignored and stale is served.Does not serve stale. Must revalidate for CDN but not for browser.
no-transformMay (un)Gzip, Polish, email filter, etc.Does not transform body.
s-maxage=delta, delta>1Same as max-ageMax-age and proxy-revalidate
immutableNot proxied downstreamProxied downstream. Browser facing, does not impact caching proxies.

Certain scenarios also affect Origin Cache-Control behavior when it is enabled or disabled.

ConditionOrigin Cache-control disabled behaviorOrigin Cache-control enabled behavior
Presence of Authorization headerContent may be cachedContent is cached only if must-revalidate, public, or s-maxage is also present
Use of no-cache headerIn logs, cacheStatus=missIn logs, cacheStatus=bypass
Origin response has Set-Cookie header and default cache level is usedContent may be cached with stripped set-cookie headerContent is not cached
Browser Cache TTL is setCache-Control returned to eyeball does not include privateIf origin returns private in Cache-Control then preserve it


Review the examples below to learn which directives to use with the Cache-Control header to control specific caching behavior.

Cache a static asset
Cache-Control: public, max-age=86400
Ensure a secret asset is never cached
Cache-Control: no-store
Cache assets on browsers but not on proxy caches
Cache-Control: private, max-age=3600
Cache assets in client and proxy caches, but prefer revalidation when served
Cache-Control: public, no-cache
Cache assets in proxy caches but REQUIRE revalidation by the proxy when served
Cache-Control: public, no-cache, proxy-revalidate or Cache-Control: public, s-maxage=0
Cache assets in proxy caches, but REQUIRE revalidation by any cache when served
Cache-Control: public, no-cache, must-revalidate
Cache assets, but ensure the proxy does not modify it
Cache-Control: public, no-transform

This configuration also disables transformation like gzip or brotli compression from our edge to your visitors if the original payload was served uncompressed.

Cache assets with revalidation, but allow stale responses if origin server is unreachable
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 (one hour). If the server returns an error instead of proper revalidation responses, Cloudflare continues serving the stale resource for a total of one minute beyond the expiration of the resource.

Cache assets for different amounts of time on Cloudflare and in visitor browsers
Cache-Control: public, max-age=7200, s-maxage=3600
Cache an asset and serve while asset is being revalidated
Cache-Control: max-age=600, stale-while-revalidate=30

This configuration indicates the asset is fresh for 600 seconds. The asset 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 passes 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.