Cache-Control is one of the most impactful HTTP headers for performance, but it is also one of the easiest to misconfigure. I built a visual builder that lets you toggle directives on and off, warns you about conflicting combinations, and generates code for Express, nginx, Apache, and CloudFront.
Tool
HTTP Cache-Control Builder
https://devnestio.pages.dev/cache-control-builder/
Features: 8 quick presets, 13 directives, conflict detection, and framework snippets.
no-store vs no-cache: The Key Distinction
This is the most common point of confusion:
| Directive | Meaning |
|---|---|
no-store |
Never store this response anywhere |
no-cache |
Store it, but revalidate with the server before using it |
no-cache does NOT mean "don't cache". It means "always check if the cache is still valid". When combined with ETag or Last-Modified, the server can return 304 Not Modified and save bandwidth.
The 8 Presets
Static assets (hashed filenames)
Cache-Control: public, max-age=31536000, immutable
When filenames include a content hash (app.a3f2c1.js), content never changes at that URL. immutable tells the browser to skip revalidation during max-age.
HTML pages
Cache-Control: public, max-age=0, must-revalidate
HTML is often the entry point for SPAs or contains dynamic content. Cache it (to get 304s) but always revalidate.
Authenticated API responses
Cache-Control: private, no-cache
private prevents CDNs from caching. no-cache forces revalidation on every request.
Never cache (sensitive data)
Cache-Control: no-store, private
CDN-heavy setup (longer CDN TTL, shorter browser TTL)
Cache-Control: public, max-age=3600, s-maxage=86400
s-maxage overrides max-age for shared caches (CDNs) only.
Stale-While-Revalidate
Cache-Control: public, max-age=60, stale-while-revalidate=86400
Serve stale content for up to 86400s while revalidating in the background. Great for content that can tolerate being slightly out of date.
Conflicts the Tool Detects
-
public+private— mutually exclusive -
no-store+no-cache—no-storeis stronger,no-cacheis redundant -
no-store+max-age—no-storeoverridesmax-age -
immutable+no-cache— contradictory ("never changes" + "always revalidate") -
must-revalidate+no-store— can't revalidate if nothing is stored
Express Implementation
// Static assets
app.use('/static', express.static('public', {
maxAge: '1y',
immutable: true,
}));
// HTML entry point
app.get('/', (req, res) => {
res.setHeader('Cache-Control', 'public, max-age=0, must-revalidate');
res.sendFile(path.join(__dirname, 'public/index.html'));
});
// Private API
app.use('/api/me', (req, res, next) => {
res.setHeader('Cache-Control', 'private, no-cache');
next();
});
nginx
# Static assets
location /static/ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
# HTML
location / {
add_header Cache-Control "public, max-age=0, must-revalidate" always;
}
# API
location /api/ {
add_header Cache-Control "private, no-cache" always;
}
Top comments (0)