← Recipes

阅读中文版本

Serve Stale Content on Origin Errors

Configure stale-if-error and stale-while-revalidate so a caching proxy keeps serving a recent copy when the origin returns 5xx or times out.

~30 mincachingcdnavailability

Task

Keep a site available during short origin outages by extending the usable lifetime of cached responses on errors.

When to use this

A marketing site sits behind a caching proxy. During a deploy the origin returns 502 for two minutes, and the proxy forwards those errors, so visitors see an error page even though a valid copy from five minutes ago is in cache.

Prerequisites

  • NGINX with proxy_cache, or a CDN that documents stale-if-error support
  • A cacheable origin response without Set-Cookie or private directives
  • Access to origin logs and cache status headers

Steps

1. Configure the cache zone and tell NGINX which upstream failures may use a stale copy.

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=web:100m inactive=7d use_temp_path=off;

server {
  location / {
    proxy_cache web;
    proxy_cache_valid 200 10m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_background_update on;
    proxy_cache_lock on;
    proxy_pass http://127.0.0.1:8080;
    add_header X-Cache-Status $upstream_cache_status;
  }
}

Expected output: Upstream 5xx responses are replaced by a cached copy when one exists, and the response is marked STALE.

2. Declare the browser and CDN freshness policy, including how long stale content may be used.

add_header Cache-Control "public, max-age=60, stale-while-revalidate=600, stale-if-error=86400" always;

Expected output: Clients and downstream caches may serve stale for one day on errors and revalidate in the background for ten minutes.

3. Stop the upstream and confirm the proxy still returns 200 from cache.

curl -sI https://www.example.com/ | grep -i -E "HTTP/|x-cache-status"

Expected output: HTTP/1.1 200 with X-Cache-Status: STALE

4. Export the stale ratio so a prolonged origin outage is visible instead of silently masked.

grep -o "X-Cache-Status: STALE" /var/log/nginx/access.log | wc -l

Expected output: A non-zero count during the outage, returning to zero after the origin recovers.

Troubleshooting

Problem

Stale content is not served after a 5xx

Solution

The response likely contains Set-Cookie or Cache-Control: private; remove those for anonymous pages and confirm proxy_cache_valid covers 200 responses.

Problem

A burst of requests hits the origin while the cache refreshes

Solution

Enable proxy_cache_lock and proxy_cache_background_update so only one request refreshes the entry.

Limitations

  • Serving stale content can violate freshness requirements for prices, inventory, or authenticated data.
  • Only GET and HEAD responses are cached; POST requests always reach the origin.

What stale-if-error buys

An origin outage does not have to become a user-visible outage. If a cached response exists and the origin fails, the proxy can return the cached copy with a warning header while it retries in the background. This trades freshness for availability. It is appropriate for public, mostly static content and inappropriate for balances, inventory counts, or anything personalized.

Configure the proxy

proxy_cache_use_stale lists the upstream conditions that permit a stale response: connection errors, timeouts, 500, 502, 503, and 504. Adding updating also serves stale while another worker refreshes the entry. proxy_cache_lock collapses concurrent misses into one origin request, which prevents a stampede when a popular key expires.

proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;

Set a bounded window

The Cache-Control header tells downstream caches how long they may use the stale copy. stale-if-error=86400 caps the exposure at one day; stale-while-revalidate=600 allows a quick background refresh. Without an explicit cap, some caches keep stale content far longer than intended. Choose the window from business tolerance, not from convenience.

Verify the failure path

Warm the cache, stop the upstream, and request the page. Expect a 200 with an X-Cache-Status: STALE header. Restart the upstream and confirm the header returns to HIT or MISS after revalidation. Log and graph stale responses; a sustained stale ratio means the origin is down, and masking that signal indefinitely defeats the purpose of monitoring.