Understanding Stale-While-Revalidate: Serving Cached Content Smartly
Stale-While-Revalidate (SWR) is a caching strategy that balances performance with the need for fresh data. The concept is simple: it's a directive to serve cached content immediately to users (even if it's recently expired) while simultaneously updating the cache with new data for future requests.
What is stale-while-revalidate?
Stale-while-revalidate essentially creates three different states for your cached content, and applies to both the browser cache and the server or CDN cache:
- 🥦 Fresh: this content that has been recently cached, and is good to serve to users
- 🫤 Stale: content that has been cached sort-of recently, is still good enough to show a user, but ought to be updated for next time
- ❌ Rotten: content that was cached too long ago to display to the user, so we send them content direct from the origin, as well as updating the cache
The key thing to remember is 'stale' does not mean 'inedible' - we are still willing to serve this cached content, but only one more time, sourcing a fresh update for future users.
Two variables define the boundaries between these states:
- max-age: this sets the limit between fresh and stale. Requests older than this age trigger a cache update.
- stale-while-revalidate: this sets the limit between stale and rotten. Only requests older than this age are fulfilled direct from the origin (usually more slowly)
The two variables are additive, so the maximum 'staleness' permitted is the sum of the two. Here's an example implemented using the cache-control header. Times are in seconds:
Cache-Control: max-age=60, stale-while-revalidate=300
This means that:
- if the cache contents are less than 60 seconds old, just display serve cached content with no further action.
- if the contents are older than 60 seconds, then for the next 300 seconds, we can display cached content if requested, but quietly update the cache for the next visit.
- if the contents are older than 360 seconds, ignore the cache contents, serve content direct from origin, and update the cache as well.
This diagram visualises the process:
Both Fran and Stacy see cached content, but only Stacy's request for stale content triggers a background revalidation. Rob, meanwhile, receives fresh content from the origin, rather than receive outdated content.
In short:
- Any request for stale or rotten content triggers a cache update, which also resets the clock (i.e: the updated cache is once again fresh)
- But only requests for rotten content require fresh content from the origin in real-time
The advantage for this method, over simply setting a max-age, is that more users receive content from the cache, which is likely much faster.
Your server only needs to handle the revalidation requests happening 'in the background', without keeping users waiting. In fact, as long as at least one user lands on a piece of 'stale' content, it will never be rotten (because the request for stale content triggers a revalidation, so the content once again becomes 'fresh') and no users will ever make requests that require interaction with the origin.
Essentially, we use requests for 'stale' content as an organic trigger for revalidation, without this also delaying the user experience.
Uses of stale-while-revalidate
Like other caching strategies, SWR is a great fit for slower-moving content types:
- API responses for certain data types: information changes occasionally but doesn't require real-time accuracy, such as user profiles, product catalogs, or configuration data.
- Static assets: CSS, JavaScript, and image files that are updated periodically but don't change frequently.
- Content Management: Blog posts, articles, or documentation pages that may receive updates but don't need instant propagation.
- Non-critical dashboards: Metrics and analytics that update regularly, but can tolerate slight delays in showing the latest information.
- Heavy requests: intense database queries or complex chains that would take an unpleasantly long time to deliver to the user in real-time
It's not such a good fit if you need your user to see up-to-the-minute information like:
- Accurate inventory levels or reservation availability
- Live auction prices or sports scores
- Success/failure/validation messages
- Session tokens and login status
- Interactive or collaborative views, where the user expects to see the live state of a document, or chat messages as-they-happen
You would not want your car's GPS to only update your location every five minutes, for example, but it would be fine if the map data itself was a few minutes old - streets don't change that fast.
Common caching timeframes reference
Once you've decided on your timeframes, you'll need to declare them in seconds. This table helps you convert common cache timelines into seconds.
Seconds | Timeframe |
---|---|
60 | 1 minute |
300 | 5 minutes |
600 | 10 minutes |
1800 | 30 minutes |
3600 | 1 hour |
7200 | 2 hours |
14400 | 4 hours |
43200 | 12 hours |
86400 | 1 day |
604800 | 1 week |
2592000 | 30 days |
7776000 | 90 days |
31556952 | 1 year |
Combining stale-while-revalidate with other instructions
- stale-if-error: Fallback to displaying cached content rather than nothing at all, if necessary. For instance
stale-if-error=604800:
will serve cached content for an additional week if, if origin errors occur - s-max-age: This sets a specific max age for CDNs (like Cloudflare) seperate to the user's browser cache. For example the directive max-age=60, s-max-age=120, would display content from the user's browser cache for one minute, and then query the CDN (which would still count as fresh) for another minute.
- private or public: These directives control the behaviour of CDNs or other proxies. If the page contains information specific to a user, use private. If everyone should see the same data, choose public. (The end-user's browser disregards these - the information can still be cached locally.)
See more commands in our guide to HTTP cache control headers.
Browser and CDN support for stale-while-revalidate
Browser support
The stale-while-revalidate instruction is understood by all modern browsers, since around 2020:
- Chrome: from version 75 onwards
- Edge: from version 79 onwards
- Safari: from version 14 onwards
- Firefox: from version 68 onwards
CDN Support
The most popular content delivery networks also recognise stale-while-revalidate directives:
- Fastly: yes - documentation
- Amazon CloudFront: yes – documentation
- Cloudflare: yes – documentation
- Vercel: yes - documentation
- KeyCDN: yes - documentation
Implementing stale-while-revalidate
There are many different layers where you can implement this caching policy, either at the server level or in the code for your web app. A few popular choices include:
- If you have an Apache server, you can update your webserver configuration or add a snippet to your .htaccess file
- If you have an Nginx server, set cache control headers in your configuration file
- PHP pages can include cache control settings in their headers
- Your CDN will base its caching behaviour on the cache-control headers it receives from the origin server, but there may be options to override these
- SWR is a React hook inspired by stale-while-revalidate behaviour
- SWRV offers similar functionality for Vue
- Service Workers can also use a stale-while-revalidate caching strategy
Monitoring your cache performance
Once you've set up your caching, you can use DebugBear to check everything is working as you'd expect. A free tool lets you perform a basic check of your website cache, while the full platform reveals cache control headers for every resource request.
The real user monitoring functionality also lets you inspect how much page weight is handled by the cache for any particular pageview.
Try it for yourself!


Monitor Page Speed & Core Web Vitals
DebugBear monitoring includes:
- In-depth Page Speed Reports
- Automated Recommendations
- Real User Analytics Data