What you'll learn
By the end of this you'll understand how CDNs work at the network level, how to use Cache-Control headers to actually control what gets cached where, what edge computing is and how it's different from a CDN cache, and when adding a CDN will hurt your system rather than help it. You'll also have a framework for thinking through the origin-CDN-user architecture for any system you're designing.
This isn't a product comparison between Cloudflare and Fastly. It's the conceptual model that lets you reason about any CDN or edge platform and make the right decisions for your specific system.
Who this is for
- Frontend and backend engineers who've added a CDN "because you're supposed to" but aren't sure exactly what it's doing
- Engineers preparing for system design interviews who need to explain CDN usage with actual depth, not just "it makes things fast near users"
- Anyone who's set a Cache-Control header and not been totally sure what they were telling the CDN to do
You can skip this if you've already designed multi-region CDN caching strategies with custom cache keys, surrogate key invalidation, and edge middleware for A/B testing or auth.
What is a CDN?
A Content Delivery Network is a distributed network of servers — called edge nodes or points of presence (PoPs) — geographically distributed around the world. When a user requests a resource your CDN serves, the request is routed to the nearest edge node rather than all the way to your origin server.
Plain English: A CDN is a copy machine spread across the globe. Instead of every user in Tokyo downloading your JavaScript bundle from a server in Virginia, they get it from a server in Tokyo. The first request at each edge node fetches from your origin; every subsequent request at that node is served from the local copy.
Simple idea: put copies of your content close to users. Control how long those copies stay fresh with Cache-Control headers. Invalidate early when you deploy changes.
Prerequisites
- Basic HTTP knowledge (request/response, headers)
- A web application serving static assets (JS, CSS, images) or public API responses
- Optional: a CDN account (Cloudflare free tier works) for hands-on practice
Setup from zero
Step 1 — Understand the request lifecycle
Before you can configure a CDN correctly, you need to understand what happens to a request:
user → DNS lookup → CDN edge node
cache hit → response served from edge (fast, no origin involved)
cache miss → edge fetches from origin → caches response → serves user
The edge node's decision to cache a response — and for how long — is primarily controlled by the Cache-Control header in your origin's response. If you don't send this header, CDNs make a guess, and the guess is often wrong.
Step 2 — Master Cache-Control headers
Cache-Control is an HTTP response header that tells caches — browser cache, CDN cache, proxies — how to handle the response.
Key directives:
max-age=N— cache for N seconds in any cache (browser and CDN)s-maxage=N— cache for N seconds in shared caches (CDN only), independent of browser cacheno-store— do not cache anywhere. Use for sensitive data.private— only the browser may cache. CDNs must not.public— explicitly cacheable by shared cachesstale-while-revalidate=N— serve stale content while fetching fresh in the background
Step 3 — Set cache lifetimes per resource type and plan invalidation
Static assets with content-hash filenames get very long cache lifetimes. HTML pages get shorter CDN caches. User-specific API responses use private or no-store.
When you deploy a bug fix or update content before TTL expires, use purge APIs or surrogate keys (cache tags) to invalidate specific cached responses across all edge nodes instantly.
> Little tip: stale-while-revalidate is one of the most useful directives you're probably not using. It tells the CDN to serve a slightly stale cached response immediately while fetching a fresh version in the background. Users get fast responses; the cache stays fresh.
The mental model
The mental model for CDN and edge is the origin is the source of truth; the edge is the delivery mechanism.
Your origin server knows the current state of everything. It has the database connection, the business logic, the user sessions. The CDN edge is a network of caches and proxies that moves responses closer to users and reduces the number of requests that have to travel all the way to the origin.
Every decision you make about CDN configuration is really a decision about how closely the edge should mirror the origin, and how quickly edge copies should expire when the origin changes. Long TTLs with infrequent invalidation mean very high cache hit rates and very low origin load — but users may see slightly stale content. Short TTLs with aggressive invalidation mean fresh content everywhere — but more origin requests and less CDN benefit.
Key terms
Edge node / Point of Presence (PoP) — a CDN server geographically distributed to be close to users.
Origin — your actual server that generates responses. The CDN sits in front of it.
Cache-Control — the HTTP response header that controls how caches handle a response.
max-age — how many seconds a response can be cached in any cache.
s-maxage — CDN-specific cache lifetime, independent of browser cache.
stale-while-revalidate — serve a slightly stale response while fetching a fresh copy in the background.
Cache invalidation / purge — explicitly removing a cached response from CDN edge nodes before its TTL expires.
Surrogate keys (cache tags) — metadata attached to cached responses that enables bulk invalidation by tag.
Edge function — code that runs at CDN edge nodes close to users, enabling fast stateless transformations without origin round-trips.
Cache hit rate — the percentage of requests served from CDN cache without hitting the origin.
Step-by-step: configure Cache-Control per resource type
Static assets (content-hash filenames):
Cache-Control: public, max-age=31536000, immutable
One year. When content changes, the filename changes, so old caches are automatically bypassed.
HTML pages:
Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=30
No browser cache; CDN caches for 60 seconds; serve stale while revalidating for 30 seconds.
Public API responses:
Cache-Control: public, s-maxage=300, stale-while-revalidate=60
User-specific data:
Cache-Control: private, max-age=60
Real-time or sensitive data:
Cache-Control: no-store
In Next.js, set headers in a route handler or middleware:
export async function GET() {
const data = await fetchPublicCatalog();
return Response.json(data, {
headers: {
"Cache-Control": "public, s-maxage=300, stale-while-revalidate=60",
},
});
}
Working examples
Surrogate key invalidation:
Surrogate-Key: product-123 category-electronics
Cache-Control: public, s-maxage=86400
When product 123 is updated, purge product-123 and all cached pages containing that product are invalidated instantly across the CDN.
Edge function for auth check (Cloudflare Workers pattern):
export default {
async fetch(request: Request): Promise<Response> {
const token = request.headers.get("Authorization");
if (!token || !await verifyJwt(token)) {
return new Response("Unauthorized", { status: 401 });
}
return fetch(request); // pass through to origin
},
};
When CDN hurts rather than helps:
❌ User-specific dashboard cached at CDN without private/no-store
❌ Real-time stock prices cached for 5 minutes
❌ POST/PUT/DELETE responses cached (should never happen)
✅ Static JS/CSS/images with content-hash filenames
✅ Public product catalog with s-maxage + surrogate key invalidation
Patterns / when to use each technique
Use long TTL + content-hash filenames when: assets only change on deploy. The filename IS the cache key.
Use s-maxage + stale-while-revalidate when: public dynamic content that changes occasionally but benefits from CDN caching (product listings, blog posts).
Use private or no-store when: responses contain user-specific or sensitive data.
Use surrogate keys when: you need long TTLs for performance but instant invalidation when specific entities change.
Use edge functions when: you need fast, stateless transformations (auth checks, geo redirects, A/B assignment) without an origin round-trip — and the logic fits within edge runtime limits (~50ms CPU).
> Little tip: Don't move application logic to edge functions just because you can. Edge function cold starts, debugging complexity, and limited runtime environments make them harder to work with than regular server functions. Move code to the edge when you have measured evidence that the latency savings are worth the operational trade-offs.
Common mistakes
Caching user-specific responses at the CDN — without private or no-store, one user's data can be served to another. This is a security incident.
No Cache-Control headers at all — CDNs guess, and the guess is often wrong in both directions.
Purging by URL when you should purge by tag — updating one product shouldn't require knowing every URL that references it. Surrogate keys solve this.
Using edge functions for database queries — edge runtimes have strict CPU limits and no persistent storage. Keep database access at the origin.
Troubleshooting
Users see stale content after deploy — TTL hasn't expired and you didn't purge. Purge by URL or surrogate key after deploy, or use content-hash filenames for static assets.
CDN cache hit rate is low — responses may have Cache-Control: no-store or private set incorrectly. Inspect response headers with curl: curl -I https://yoursite.com/api/catalog.
Authenticated content leaked to other users — CDN cached a response that should have been private. Audit all API responses for correct Cache-Control directives immediately.
Edge function timing out — edge runtimes have strict CPU limits (typically 50ms). Move heavy logic to the origin; keep edge functions for fast stateless checks only.
Checklist
- [ ] Cache-Control headers set explicitly on all responses (never rely on CDN defaults)
- [ ] Static assets use content-hash filenames with long
max-ageandimmutable - [ ] User-specific responses use
privateorno-store - [ ] Public dynamic content uses
s-maxagewithstale-while-revalidate - [ ] Surrogate keys configured for entities that need instant invalidation
- [ ] Purge procedure documented and tested for deploys
- [ ] CDN cache hit rate monitored
- [ ] Edge functions limited to stateless, fast transformations (no database queries)
Practice task
Configure Cache-Control headers for three resource types in a project: static assets (1-year immutable), a public API endpoint (5-minute CDN cache with stale-while-revalidate), and a user profile endpoint (private, no CDN caching). Deploy behind a CDN, verify headers with curl -I, and confirm cache behavior by checking response times from different geographic locations and inspecting CDN cache status headers (CF-Cache-Status, X-Cache, or equivalent).
FAQ
Cloudflare vs Fastly vs Vercel Edge — which should I use?
Cloudflare is the easiest starting point with a generous free tier and global edge network. Fastly offers more granular cache control and surrogate key support for complex invalidation. Vercel Edge integrates tightly with Next.js deployments. The concepts in this guide apply to all three; pick based on your deployment platform and invalidation needs.
Should I cache HTML pages at the CDN?
Yes, with short s-maxage and stale-while-revalidate. HTML pages link to your current asset filenames and change more frequently than static assets. A 60-second CDN cache with background revalidation gives fast responses without serving hours-old pages.
What's the difference between a CDN and edge computing?
A CDN caches and serves responses. Edge computing runs your code at edge nodes — auth checks, redirects, response transformations — before the request reaches your origin. Edge functions are an extension of the CDN, not a replacement for your origin server.
What to learn next
With CDN fundamentals solid: application-level caching for database-backed responses that can't be cached at the edge, rate limiting at the CDN/gateway layer to reject abuse before it reaches your origin, and database read replicas to handle the origin load that remains after CDN caching.
Related on Baseline
- System design: caching — application-level caching for data that can't be cached at the CDN edge
- System design: rate limiting — reject abuse at the CDN/gateway before it reaches your origin
- System design: databases — read replicas for origin load that remains after CDN caching
Takeaways
A CDN moves content closer to users and takes load off your origin, but only when you control caching with explicit Cache-Control headers. Match cache strategy to content type: immutable long TTL for static assets, short s-maxage with stale-while-revalidate for public dynamic content, private or no-store for user-specific data. Surrogate keys give you long TTLs with instant invalidation.
If you remember only one thing: the origin is the source of truth and the edge is the delivery mechanism — every CDN decision is about how closely the edge mirrors the origin and how quickly it catches up when things change.