Does stale-while-revalidate work?

@cs-cf, what you’re saying is not accurate; I can show this with a test case.

SWR Doesn’t Affect Parallel Requests on Cloudflare

You’re quoting the documentation saying that SWR allows stale content to be returned after the content has expired, but only during a slow revalidation. Which is to say, after the content expires, a first request X will get a cache miss and slowly revalidate the content, but a second request Y arriving during the revalidation request X will receive fast, stale content.

That’s not what SWR does. What the documentation describes is, in fact, the default behavior of Cloudflare, even if you don’t pass a SWR setting at all. I believe I can prove this with an additional test case.

I’ve configured a URL at https://www.choiceofgames.com/swr/test2.js, designed to sleep for 30 seconds before replying with this cache header:

cache-control: public, max-age=30

You can curl that URL to get a cache hit from your local POP; subsequent requests in less than 30 seconds will return quickly and show the cached timestamp. Now wait 30 seconds for the data to expire. In two tabs, curl the content again in the first tab, and then curl the content again in the second tab.

If the documentation were right: lacking a SWR header, the second tab’s request should also get a cache miss and revalidate the content, taking at least 30 seconds to return.

Actual behavior: Without a SWR header, the second tab receives fast, stale content, with the old timestamp, and the response header cf-cache-status: UPDATING. (Adding a SWR=30 header has no effect, either, because that’s what Cloudflare already does by default.)

UPDATING is not a cache status we return.

That is absolutely not correct, as my test case shows.

SWR Is for Launching Revalidation Requests in the Background, Not Just for Parallel Requests

The purpose of SWR is to ensure that all requests within the SWR interval receive fast, cached content, while ensuring that the proxy frequently updates its cache in the background.

For example, this is a good and useful Cache-Control header with SWR: cache-control: public, max-age=0, stale-while-revalidate=600. Here’s what that should do:

  1. An initial request X comes in for the content at time T=0; a cache miss. Despite the max-age=0 setting, the content should be stored in the proxy cache for at least 600 seconds, because of the SWR setting.
  2. A subsequent (non-parallel) request Y comes in for the content at time T=590. The proxy immediately returns the stored, stale content from the proxy cache of the X request. Without forcing request Y to wait any longer, the proxy also kicks off its own request in the background to revalidate the content. Now that the content has revalidated, it’s valid for another 600 seconds.
  3. A subsequent request Z comes in at T=610. Since the content was revalidated at T=590, the proxy can now return the stale content from the background request Y until T=1190, so the proxy returns the stored content, and again revalidates the content in the background. It’s now valid for another 600 seconds, until T=1210.
  4. Another request W comes in at T=1220. At last the content is “truly stale” (as the RFC calls it); it’s no longer safe to return this stale content. The content is revalidated while request W waits for a response; once revalidated, the content is stored in the proxy cache, and is now safe to return stale until T=1820.

You may enjoy reading Steve Souders writing about this on Fastly’s blog.

https://www.fastly.com/blog/stale-while-revalidate-stale-if-error-available-today

By specifying stale-while-revalidate, users aren’t slowed down when a response cached at the CDN needs to be revalidated.

It’s for all requests during the SWR interval, not just parallel requests.

“MAY” Just Means SWR Is Entirely Optional for Proxies

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).

But in the case that the proxy ignores SWR, it’s not correct to say that Cloudflare “supports” SWR. To honor SWR, that would mean that Cloudflare would return stale content during the SWR interval, even to non-parallel requests.

I recognize that a distributed proxy like Cloudflare cannot and does not guarantee a cache hit, even within the max-age internal, but Cloudflare is not doing what SWR is supposed to do even within a single POP.