# Stale-while-revalidate configured for APIs

- **Pattern:** `ab-002029` (`performance-deep-dive.caching-cdn.stale-while-revalidate-apis`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002029
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002029)

## Why it matters

Every read-only API endpoint that responds with `no-cache` forces the browser to wait for a full network round-trip before rendering data — even for data that changes infrequently. `stale-while-revalidate` flips this: the browser serves the last cached response immediately (zero perceived latency), then fetches a fresh copy in the background. ISO 25010:2011 time-behaviour measures the round-trip cost; the user-visible gain is instant page paint for the common case, with fresh data arriving shortly after.

## Severity rationale

High because serving all API responses with `no-cache` forces users to wait for a full network round-trip on every data fetch, adding hundreds of milliseconds to perceived load time even when cached data would be adequate.

## Remediation

Add `Cache-Control: public, max-age=60, stale-while-revalidate=86400` to API responses where serving slightly stale data is acceptable. Exclude personalized or mutation endpoints — those must use `private, no-cache`.

```ts
// app/api/products/route.ts
export async function GET() {
  const products = await fetchProducts()
  return Response.json(products, {
    headers: { 'Cache-Control': 'public, max-age=60, stale-while-revalidate=86400' },
  })
}
```

## Detection

- **ID:** `stale-while-revalidate-apis`
- **Severity:** `high`
- **What to look for:** Count all API endpoints. Enumerate which return `stale-while-revalidate` in their Cache-Control headers vs. `no-cache`. Examine API response headers or middleware/service worker code for the `stale-while-revalidate` directive. Check for patterns where cached data is served immediately while a fresh fetch happens in the background.
- **Pass criteria:** API endpoints use `Cache-Control: public, max-age=60, stale-while-revalidate=86400` (or equivalent). Users receive cached data instantly while fresh data is fetched in background. At least 50% of read-only API endpoints should use stale-while-revalidate.
- **Fail criteria:** API responses do not use stale-while-revalidate. Users wait for network fetch even if cached data is available.
- **Skip (N/A) when:** The project has no API endpoints or all API responses are highly dynamic and not cacheable.

- **Cross-reference:** For edge caching of personalized content, see the `edge-cache-personalization` check.
- **Detail on fail:** `"API endpoints use Cache-Control: no-cache — cached data never served, users always wait for fresh fetch"` or `"No Cache-Control header on API responses"`
- **Remediation:** Add stale-while-revalidate to API responses so users get instant cached data while fresh data loads in background.

  ```ts
  // app/api/products/route.ts — stale-while-revalidate caching
  return Response.json(data, { headers: { 'Cache-Control': 'public, max-age=60, stale-while-revalidate=86400' } })
  ```

## External references

- iso-25010 time-behaviour

Taxons: performance

HTML version: https://auditbuffet.com/patterns/ab-002029
