Every request that has to travel all the way back to your origin server is a request that's slower than it needs to be — CDN caching exists to serve as much as possible from a location physically closer to the user, and correctly, without ever hitting your origin at all.
A CDN (Content Delivery Network) caches content at edge locations distributed globally, serving requests from the location nearest the user rather than routing every request back to a single origin server. Getting real benefit from a CDN depends on correctly configured cache headers telling it what can be cached, for how long, and how to invalidate it when content changes.
Why CDN Caching Matters (and When to Skip It)
Serving from an edge location physically close to the user cuts network latency significantly compared to a single origin server, especially for a geographically distributed user base. It also reduces load on your origin server entirely for cached requests, improving both performance and resilience under traffic spikes.
Skip aggressive CDN caching configuration for highly dynamic, per-user content that can't be meaningfully cached anyway — not everything benefits from CDN caching, and misconfiguring cache headers on genuinely dynamic content risks serving stale or incorrect data to users.
Getting Started with CDN Caching
Setting cache headers for static assets (long cache, immutable):
Cache-Control: public, max-age=31536000, immutable
For content that changes but can be cached briefly, with revalidation:
Cache-Control: public, max-age=60, stale-while-revalidate=3600
In Next.js, controlling caching for API routes or fetches:
export async function GET() {
const data = await fetchData();
return Response.json(data, {
headers: { "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300" },
});
}
Core CDN Caching Concepts Every Developer Should Know
immutable assets (with content-hashed filenames) can be cached essentially forever. Build tools that add a content hash to filenames (app.a1b2c3.js) let you set a very long max-age safely — since any content change produces a new filename, there's no staleness risk, and the browser/CDN never needs to revalidate.
stale-while-revalidate serves cached (possibly stale) content immediately while refreshing it in the background. This gives users fast responses even for content that changes periodically, without them ever waiting on a cache miss — a strong default for semi-dynamic content like a blog listing or product catalog.
s-maxage controls CDN/shared cache behavior separately from browser cache behavior (max-age) — letting you cache more aggressively at the CDN layer (which you control invalidation for) than in individual users' browsers (which you generally can't invalidate on demand).
Cache invalidation is the hard part, not caching itself. Purging or invalidating a CDN cache when underlying content changes (a new blog post published, a product price updated) needs an explicit strategy — either short TTLs with revalidation, or an active purge call triggered by the content change itself.
await fetch(`https://api.cdn-provider.com/purge`, {
method: "POST",
body: JSON.stringify({ urls: [`/blog/${slug}`] }),
});
Common CDN Caching Mistakes and How to Fix Them
Mistake 1: no cache headers at all, letting the CDN's default behavior (often minimal caching) apply. This leaves significant performance on the table. Fix: explicitly set Cache-Control headers appropriate to each content type's actual freshness requirements.
Mistake 2: caching genuinely dynamic, per-user content publicly, serving one user's cached response to another. This is a real correctness and potentially security bug, not just a performance issue. Fix: use private or no-store for per-user content, reserving public caching for content that's the same for all users.
Mistake 3: no invalidation strategy, leaving content stale after updates with no way to force a refresh. Fix: implement either short TTLs with revalidation or explicit purge calls tied to your content update flow.
When Should You Use Long CDN Caching Instead of Short TTLs?
Use long caching (with immutable where applicable) for static, versioned assets that never change under a given URL — build output, images with content-hashed filenames. Use short TTLs with stale-while-revalidate for content that changes periodically but where slightly stale data is acceptable — blog listings, product catalogs, most read-heavy API responses.
CDN Caching in Production
Set cache headers deliberately per content type rather than relying on defaults, and verify with actual response headers (not assumptions) that caching is behaving as intended. Also build an explicit invalidation path into your content update flow for anything cached longer than a few minutes, so published changes don't sit behind a stale cache unexpectedly.
If your API responses or pages currently have no explicit Cache-Control headers, that's a quick, high-leverage fix — start with your most frequently requested, least frequently changing content.