patternjavascriptModerate
Browser caching headers: Cache-Control strategy for static vs dynamic resources
Viewed 0 times
Cache-Controlmax-ageimmutableno-cacheno-storebrowser cachingCDNcache headers
Problem
Static assets use short or no cache TTLs, forcing re-downloads on every visit. Dynamic HTML is cached too aggressively and users see stale content. Neither extreme is optimal.
Solution
Use content-hashed filenames for static assets with a long cache TTL, and short or no-store for HTML documents.
# Static assets with hash in filename (e.g., main.a1b2c3d4.js)
Cache-Control: public, max-age=31536000, immutable
# HTML documents (no hash, must be fresh)
Cache-Control: no-cache
# no-cache means: always revalidate with server
# The browser still stores a copy and can use 304 Not Modified
# API responses
Cache-Control: private, max-age=60
# Security-sensitive pages
Cache-Control: no-store
// In Express
app.use('/static', express.static('dist', {
maxAge: '1y',
immutable: true,
}));
# Static assets with hash in filename (e.g., main.a1b2c3d4.js)
Cache-Control: public, max-age=31536000, immutable
# HTML documents (no hash, must be fresh)
Cache-Control: no-cache
# no-cache means: always revalidate with server
# The browser still stores a copy and can use 304 Not Modified
# API responses
Cache-Control: private, max-age=60
# Security-sensitive pages
Cache-Control: no-store
// In Express
app.use('/static', express.static('dist', {
maxAge: '1y',
immutable: true,
}));
Why
Content-hashed filenames mean the URL changes every time the file changes. max-age=31536000 (1 year) is safe because browsers will request the new URL. HTML with no-cache ensures users always get the latest routing and script references.
Gotchas
- immutable is a hint to the browser to not even attempt revalidation within max-age — saves conditional request overhead
- no-cache is NOT the same as no-store: no-cache stores but revalidates, no-store does not store at all
- CDN caches respect Cache-Control but may need explicit purging on deployment
- s-maxage overrides max-age for shared caches (CDNs) while keeping a shorter max-age for browsers
Code Snippets
Express middleware for per-type Cache-Control headers
// Express middleware: different Cache-Control per content type
app.use((req, res, next) => {
if (req.path.match(/\.(js|css|woff2|png|webp)$/)) {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
} else if (req.path === '/' || req.path.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache');
}
next();
});Context
When configuring HTTP caching headers for a new deployment or diagnosing why users see stale content
Revisions (0)
No revisions yet.