Practical caching strategies
A plain-language guide to caching layers — client headers, server caches, and CDN rules — for consistent response times.

Caching is easy to get wrong and expensive to get right. Done well, it cuts latency and bandwidth dramatically; done badly, it serves stale data and creates confusing bugs. This post covers the main caching layers and the decisions that make each one safe.
1. Client (browser) caching
Controlled by response headers. The browser stores assets so repeat visits are instant.
http
Cache-Control: public, max-age=3600, must-revalidate
ETag: "abc123"
- `max-age` — how long the cached copy is considered fresh.
- `ETag` / `Last-Modified` — lets the browser revalidate cheaply with a `304 Not Modified`.
Use long max-age with content hashing (e.g., app.4f2a.js) so new versions get new URLs.
2. Server-side caching
Keep computed results in memory so you don't repeat work:
- **In-memory** (Map, Redis) for expensive computations.
- **Full-page** caches for mostly-static responses.
- **Database query** caches for hot reads.
ts
const cache = new Map<string, { value: unknown; expires: number }>();
function getCached(key: string, ttlMs: number, compute: () => unknown) {
const hit = cache.get(key);
if (hit && hit.expires > Date.now()) return hit.value;
const value = compute();
cache.set(key, { value, expires: Date.now() + ttlMs });
return value;
}
For production, prefer Redis over a single-server Map so caches are shared and survive restarts.
3. CDN caching
A CDN caches at the edge, close to users. Rules to know:
- Cache `GET` responses; never cache `POST`/auth-specific ones.
- Use `stale-while-revalidate` to serve old content while refreshing.
- Purge on deploy for HTML and on data change for APIs.
4. Invalidation is the hard part
The two hardest problems in CS: naming, off-by-one errors, and cache invalidation. Strategies:
- **Time-based**: short TTLs so staleness is bounded.
- **Event-based**: purge a key when its data changes.
- **Versioned keys**: change the key when the value should change.
| Approach | Freshness | Complexity |
|---|---|---|
| Long TTL | Lower | Low |
| Short TTL | Higher | Low |
| Explicit purge | Highest | Medium |
| Versioned key | Highest | Medium |
5. A small case study
Moving cache decisions closer to the edge — caching a product list at the CDN with a 60-second TTL and purging on price change — cut origin traffic by ~70% and dropped p95 latency from 480ms to 90ms. The key was choosing a TTL short enough that stale prices were rare, and a purge event precise enough that updates propagated immediately.
Rules of thumb
- Cache reads more than writes.
- Never cache personalized responses without per-user keys.
- Always have a way to invalidate.
- Measure hit rate; a low hit rate means you're caching the wrong thing.
Caching is a lever, not a cure. Pull it where reads are hot and staleness is tolerable.