Understanding origin cache control

I’ve setup a page rule for my domain to use origin cache control and I’m serving Cache-Control headers that look like this: Cache-Control: public, max-age=60, stale-while-revalidate=600, stale-if-error=86400

But the content does not seem to be getting cached at all, cloudflare sends the CF-Cache-Status: DYNAMIC header for all requests. Is 60 seconds too short or is there something else I need to do to make cloudflare respect my cache headers?

Quite likely. Though I’d still expect it to return a different status. What if you increase “max-age” to a more reasonable value?

Thanks for the response. I’ve tried 120s now, same problem. I think 2 minute cache is pretty reasonable with a stale-while-revalidate of 10minutes, is there anything else I can try to debug this?

Post the URL that you want cached.

HTTP/1.1 200 OK
CF-Cache-Status: DYNAMIC
CF-RAY: 52120ccbda97d915-AMS
Cache-Control: public, max-age=120, stale-while-revalidate=600, stale-if-error=86400
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Sat, 05 Oct 2019 20:12:22 GMT
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
Set-Cookie: __cfduid=d55b38317976a39fc14b3cc5bbd1090921570306341; expires=Sun, 04-Oct-20 20:12:21 GMT; path=/; domain=[redacted]; HttpOnly; Secure
Transfer-Encoding: chunked
Vary: Accept-Encoding

Thats not something that Cloudflare will cache by default. Do you have a page rule that will cache it?

Yes I have a page rule for that domain with Origin Cache Control: On

That does not affect the proxy cache. You will need to set the cache level too.

Tried setting a cache level, no effect. Page rule now reads Cache Level: Standard, Origin Cache Control: On and the matching pattern is *mydomain.com/*

Not standard of course, you will need cache everything.

Post a screenshot of your page rules.

1 Like

Ah! Ok with cache everything it works :raised_hands: Thanks!

It seems stale-while-revalidate isn’t respected though, stale content is never served

That appears to be an experimental directive at this point and I wouldnt be surprised if it is neither supported by Cloudflare nor possibly applicable to Cloudflare’s edge cache at all.

tried in page rule setting Always Online = Disabled ?


Tried with rule:

Always Online: Off, Cache Level: Cache Everything, Origin Cache Control: On

and cache header:

Cache-Control: public, max-age=60, stale-while-revalidate=600, stale-if-error=86400

Same behaviour, after 60s I get CF-Cache-Status: EXPIRED and my server is hit (synchronously via same request it looks like by the response time increase)

@sandro: This guide suggest it would be supported

It puts an emphasis on “may”.

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.

I’d open a support ticket at this point.

1 Like

you’re confusing the different caches - cloudflare cache vs browser cache. max-age and cache control headers are to control browser level cache and not cloudflare’s cache

to confirm max-age = 60 second browser level cache is working check response header for the file/page in browser developer tools/network tab

s-max-age value controls shared proxies like cloudflare’s cache


The “max-age” response directive 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.


The “s-maxage” response directive 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.

AFAIK s-maxage is just an override for max-age if you want different behaviour in browser and shared caches. I’m only speaking about the cloudflare cache here not any browser caches.

The cache headers I’m serving has the expected behaviour in Nginx using proxy_cache when setting proxy_cache_background_update on; FYI. There I get stale content on a miss after 60s (with fresh content on a subsequent request) and it will not hit my server synchronously until the cache has been stale for 10min

You might need to contact cloudflare tech support and ask them under which circumstances other than Always Online, where stale-while-revalidate is not respected by cloudflare cache.

there was a similar question to yours in which cloudflare staff @cs-cf replied at Does stale-while-revalidate work?

When present in an HTTP response, the stale-while-revalidate Cache-Control extension indicates that caches MAY serve the response in which it appears after it becomes stale. It doesn’t mean we can’t go to the origin to get a copy and serve it instead. But if there are other waiting requests for the same content we will serve the stale response i believe.

I do like RFCs for their precision in language. Note the use of the term MAY. For the fist subsequent request for an asset at a POP we will go to the origin to revalidate. If there are subsequent requests for that asset before we have revalidated it we will not make additional trips to the origin or wait on the response from the initial requery, but will instead serve the stale asset to those requests.

So to demonstrate it working you would need multiple requests for the same asset in the same POP for an object which took long enough to revalidate that between the time we went off to the origin to validate the first request in the stale-while-revalidate window and the time we retrieved the new object those additional requests could be served the stale version.

The post by @dfabulich in the thread you linked sums up the situation correctly:

SWR is an optional extension to Cache-Control; that’s what’s meant by the “MAY” in the RFC. You can be a compliant HTTP proxy while completely ignoring SWR (as, indeed, Cloudflare does).

This guide should be updated to say that instead of confusing users by implying it is supported.