Mark Mwangi logoMark
Mwangi
Return to home
Get in
touch
Admin
login
PerformanceSeptember 1, 20259 min read

Practical caching strategies

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

By Mark Mwangi
Practical caching strategies cover coverImage

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.
ApproachFreshnessComplexity
Long TTLLowerLow
Short TTLHigherLow
Explicit purgeHighestMedium
Versioned keyHighestMedium

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.

CachingEdgeCDN
Go back to all blogs →Go to all projects →Contact →

Related Articles

  • Designing for subtle performance gains
  • Working well with remote teams
  • How to Write Quality Git Commits

About

Mark Mwangi

Creating secure and meaningful digital experiences.

Software developer focused on secure, responsive, and high-quality digital experiences.

Socials

GitHubLinkedInXE-mailinstagramfacebookwhatsappMedium

© 2026 Mark Mwangi | All rights reserved

Terms of Service•Privacy Policy

Last updated: April 30, 2026 16:23:51 UTC