HTTP Caching Explained
Proper caching makes sites fast. Here's how HTTP caching works and how to use it.
Why Caching
Without caching: Every request hits your server. Every asset redownloads.
With caching: Browsers store responses. Subsequent requests are instant.
Cache-Control Header
The main way to control caching:
Cache-Control: max-age=3600
"Cache this for 1 hour (3600 seconds)."
Directives
max-age=N # Cache for N seconds
no-cache # Must revalidate before using cached version
no-store # Don't cache at all (sensitive data)
private # Only browser can cache (not CDNs)
public # Anyone can cache (including CDNs)
immutable # Never changes (with versioned URLs)
Static Assets
For JS, CSS, images with hashed filenames:
Cache-Control: public, max-age=31536000, immutable
One year. The hash changes when content changes, so it's safe.
// Express
app.use('/static', express.static('public', {
maxAge: '1y',
immutable: true
}));
HTML Pages
Usually don't cache aggressively:
Cache-Control: no-cache
Browser must check if content changed before using cached version.
API Responses
Depends on the data:
// User-specific data
res.set('Cache-Control', 'private, no-cache');
// Public data that updates hourly
res.set('Cache-Control', 'public, max-age=3600');
// Real-time data
res.set('Cache-Control', 'no-store');
ETags
Content fingerprint for validation:
Response:
ETag: "abc123"
Next request:
If-None-Match: "abc123"
Response (if unchanged):
304 Not Modified
Browser uses cached version if ETag matches. No body transferred.
Express Example
const express = require('express');
const app = express();
// Static files with long cache
app.use('/assets', express.static('dist/assets', {
maxAge: '1y',
immutable: true
}));
// API with short cache
app.get('/api/products', (req, res) => {
res.set('Cache-Control', 'public, max-age=300'); // 5 minutes
res.json(products);
});
// No cache for user data
app.get('/api/me', auth, (req, res) => {
res.set('Cache-Control', 'private, no-store');
res.json(req.user);
});
CDN Caching
CDNs (Cloudflare, Vercel, etc.) cache based on these headers too.
Cache-Control: public, s-maxage=3600, max-age=300
- `s-maxage`: CDN caches for 1 hour
- `max-age`: Browser caches for 5 minutes
Testing
curl -I https://example.com/style.css
Look for `Cache-Control`, `ETag`, `Last-Modified` headers.
Common Patterns
| Content | Cache-Control | |---------|---------------| | Versioned assets | `public, max-age=31536000, immutable` | | HTML pages | `no-cache` or short `max-age` | | API (public) | `public, max-age=300` | | API (private) | `private, no-cache` | | Real-time data | `no-store` |
Get caching right and your site feels instant.