Cache Control: Advanced HTTP Caching Directives for Modern APIs
In this tutorial, you will learn about Cache Control: Advanced HTTP Caching Directives for Modern APIs. We cover key concepts, practical examples, and best practices to help you master this topic.
Beyond basic max-age and public, modern HTTP caching offers advanced directives that enable sophisticated cache behaviors: serving stale content during revalidation (stale-while-revalidate), caching on errors (stale-if-error), and declaring immutability for versioned assets.
flowchart LR
subgraph Freshness
A[max-age=60]
B[s-maxage=300]
end
subgraph Stale While Revalidate
C[SWR=3600]
D[SIE=86400]
end
subgraph Immutable
E[immutable]
end
A -->|Fresh| F[Serve Cache]
A -->|Stale| C
C -->|Background Revalidation| G[Return Stale Immediately]
C -->|Revalidated| F
A -->|Error| D
D -->|Serve Stale on 5xx| H[Return Stale Cached]
E -->|Versioned Asset| I[Cache Forever]
What You'll Learn
- The stale-while-revalidate and stale-if-error directives
- The immutable extension for fingerprinted assets
- CDN-specific headers: Cloudflare CDN-Cache-Control, Akamai Edge-Control
- Surrogate-Control for reverse proxy differentiation
Why It Matters
Stale-while-revalidate dramatically improves perceived performance by serving instant responses while fetching fresh data in the background. This pattern is essential for APIs where users tolerate slightly stale data for the benefit of speed.
Real-World Use
A news API sets Cache-Control: public, max-age=10, stale-while-revalidate=86400. Readers see articles that are up to 10 seconds old instantly. In the background, the browser fetches fresh data. If the API is down, content stays available for up to 24 hours.
Advanced Directives
Stale-While-Revalidate
app.get('/api/news', async (req, res) => {
res.set('Cache-Control', 'public, max-age=30, stale-while-revalidate=3600');
const news = await db.getLatestNews();
res.json(news);
});
Expected output:
Within 30s: cached fresh. Between 31s and 3630s: cache serves stale immediately, fetches update in background.
Stale-If-Error
app.get('/api/weather/:city', async (req, res) => {
res.set('Cache-Control', 'public, max-age=60, stale-if-error=86400');
try {
const weather = await weatherService.fetch(req.params.city);
res.json(weather);
} catch (err) {
// If cache exists and we get here, CDN/browser will serve stale cached version
throw err;
}
});
Expected output:
If the weather service is down, CDN serves the cached response (up to 24h old) instead of showing an error.
Immutable for Versioned Assets
app.get('/static/js/bundle.a1b2c3.js', (req, res) => {
res.set('Cache-Control', 'public, max-age=31536000, immutable');
res.sendFile(bundlePath);
});
Expected output:
Browser caches this file for 1 year. The immutable directive tells browsers not to revalidate on page reload — the file never changes because the filename includes a content hash.
Surrogate-Control for CDN Differentiation
app.get('/api/popular', (req, res) => {
res.set('Cache-Control', 'private, max-age=0');
res.set('Surrogate-Control', 'public, max-age=300');
res.json(getPopular());
});
Expected output:
Browser does not cache (private). CDN caches for 300 seconds (Surrogate-Control), reducing origin load for popular content.
Common Mistakes
- Using
stale-while-revalidatewithoutmax-age— the stale period has no effect if the resource never becomes stale. - Setting
max-agetoo high for frequently changing content, forcing background revalidation every time. - Forgetting that
stale-if-erroronly works if a valid cached response exists before the error occurs. - Using
immutableon non-fingerprinted assets — if the URL doesn't change with content, immutable prevents users from seeing updates. - Mixing
Cache-Controlwith Pragma or Expires inconsistently, causing confusing cache behavior in older browsers.
Practice Questions
- What happens when stale-while-revalidate expires?
- How does stale-if-error protect against backend failures?
- Why should you only use immutable on content-hashed or versioned URLs?
- What is the difference between Surrogate-Control and Cache-Control?
- How does a CDN behave when Cache-Control says private but Surrogate-Control says public?
Challenge
Design a caching policy for a live sports scores API. Updates arrive every 5 seconds. Users should see scores instantly (max-age 1s), tolerate 30-second stale scores while revalidating, and if the origin fails, serve scores up to 1 hour old.
FAQ
Mini Project
Create an Express server with three endpoints demonstrating stale-while-revalidate, stale-if-error, and immutable. Use curl to verify behavior. For the stale-if-error demo, simulate a backend failure after initial cache fill and confirm the stale response is served.
What's Next
Continue with CDN Caching to learn how content delivery networks cache and serve content at the edge.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro