A fast cache does not always need to return an error as soon as a stored response expires or the origin briefly fails. The stale-while-revalidate and stale-if-error directives permit controlled reuse of an old response: one hides refresh latency, while the other favors availability during an origin failure.
They are not a license to cache everything. Safe use requires an explicit decision about which data may be old, the maximum acceptable staleness, the cache layer enforcing the policy, and the evidence showing what real users receive.
1. Freshness, staleness, and validation
Under RFC 9111 HTTP Caching, a response is fresh while its age has not exceeded its freshness lifetime. It then becomes stale. Stale does not prove that the content is wrong; it means the cache cannot normally reuse it without validation or explicit permission to serve stale.
max-age defines freshness lifetime in seconds. Age estimates time since generation or successful validation. Conditional requests can validate a response through ETag/If-None-Match or Last-Modified/If-Modified-Since. A 304 Not Modified response confirms reuse without retransmitting the full body.
| State | Typical cache action | User effect |
|---|---|---|
| Fresh | Reuse without contacting origin | Fast cached response |
| Stale, validation required | Contact origin before reuse | May wait for origin |
| Stale allowed by SWR | Serve old response and refresh in background | No validation delay inside the window |
| Stale allowed by SIE | Serve old response after a qualifying error | Old content instead of a hard failure |
2. How stale-while-revalidate hides latency
RFC 5861 defines stale-while-revalidate so a cache may serve a stale response for the indicated number of seconds while attempting non-blocking validation:
Cache-Control: public, max-age=60, stale-while-revalidate=30
- Seconds 0–60: the response is fresh.
- For the next 30 seconds: the cache may immediately serve stale and revalidate in the background.
- After successful refresh: the new response replaces the old one and age resets.
- After the window closes without a usable refresh: SWR no longer automatically permits stale reuse.
Background refresh is commonly triggered by traffic during the stale window. A low-traffic URL might receive no such request. Browsers, reverse proxies, and CDNs also differ, so test the actual delivery layer rather than inferring behavior from the origin header.
3. How stale-if-error improves resilience
Cache-Control: public, max-age=60,
stale-while-revalidate=30, stale-if-error=600
After 60 seconds the stored response is stale. If the origin fails within the 600-second SIE window, the cache may reuse the previous success instead of forwarding a hard error. RFC 5861 discusses server, DNS, and network failures, while CDN products can support a narrower set. For example, Cloudflare Origin Cache Control documents 500, 502, 503, and 504 behavior along with product-specific limitations.
SWR optimizes latency; SIE optimizes availability. They can share a header but address different events.
4. Choose TTLs from a staleness budget
The values form a business contract about how old an answer may become:
- Versioned static assets: can usually remain cached for a long time because a content change creates a new URL.
- Public articles or category pages: may tolerate a short background-refresh window and a bounded fallback.
- Prices, inventory, and order state: must match business promises and may be unsuitable for stale reuse.
- Authorization, balances, and one-time codes: should not return an old answer merely to avoid an error.
With both max-age=300 and SWR set to 300, a user might receive content nearly ten minutes after the previous validation. A one-day SIE period is a product and operations decision, not just a performance tweak.
5. Prevent private-data leaks through shared caches
public can allow shared storage when other conditions are met. Do not attach it to personalized responses unless the cache key covers every required variant. Cookies, Authorization, locale, tenant, and feature flags can all change the body.
- Use
privatefor user-private caches. - Use
no-storewhen storage is forbidden. - Review
Varyand CDN cache keys without creating unbounded variants. - Never treat caching headers as authorization.
- Do not shared-cache Set-Cookie responses without understanding provider behavior.
RFC 9111 identifies directives such as no-cache and must-revalidate that can prohibit stale serving in applicable contexts. Conflicting headers may produce conservative behavior. Inspect the final response after every proxy layer.
6. Application and proxy examples
header('Cache-Control: public, max-age=60, stale-while-revalidate=30, stale-if-error=600');
header('ETag: "'.$contentHash.'"');
location /public-content/ {
add_header Cache-Control "public, max-age=60, stale-while-revalidate=30, stale-if-error=600" always;
proxy_pass http://app_backend;
}
These examples only set response policy. Adding a header does not itself enable Nginx proxy caching. A CDN might also use an edge TTL or cache rule different from the browser TTL.
7. Test a timeline, not a single curl request
- Select a public test URL and short TTL in a non-production environment.
- Prime the cache; record Date, Age, ETag, and provider cache status.
- Request while fresh and confirm unexpected origin calls do not occur.
- Request after max-age but within SWR; measure latency and observe validation.
- Change origin content and confirm the fresh representation arrives within the agreed period.
- Simulate controlled origin failures; confirm SIE stops after its window.
- Exercise purge and rollback without deliberately breaking production.
curl -sS -D - -o /dev/null https://example.test/public-content/article
# Inspect Cache-Control, Age, ETag, Cache-Status,
# or the provider-specific cache header.
RFC 9211 defines Cache-Status, but providers can expose proprietary headers instead. Combine edge evidence with origin logs, request IDs, hit ratio, and origin request rate.
8. Production checklist
- Classify public, private, and non-storable URLs.
- Document freshness lifetime and maximum accepted staleness.
- Provide ETag or Last-Modified validators where appropriate.
- Inspect final headers after reverse proxies and CDNs.
- Test validation, timeout, 5xx, DNS, and network behavior supported by the platform.
- Monitor Age, hit ratio, origin request rate, and stale responses.
- Prepare purge, rollback, and a rapid stale-disable procedure.
Stale caching works when “old” is budgeted and observable. Start with low-risk public content, verify behavior at the CDN actually in use, and expand only with evidence. Performance and resilience must not compromise correctness or privacy.




No comments yet. Be the first to share your thoughts.