Every millisecond of latencyWhat is latency?The time delay between sending a request and receiving the first byte of the response, usually measured in milliseconds. costs you users. Research consistently shows that a 100ms increase in load time reduces conversions. A CDNWhat is cdn?Content Delivery Network - a network of servers around the world that caches your files and serves them from the location closest to the user, making pages load faster. is the single highest-leverage infrastructure change you can make for a globally distributed user base, it moves your content physically closer to your users and absorbs most of your traffic before it ever reaches your servers.
How CDNs work
The originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443. and the edge
Without a CDNWhat is cdn?Content Delivery Network - a network of servers around the world that caches your files and serves them from the location closest to the user, making pages load faster., every request goes all the way to your origin server, regardless of where the user is. A user in Tokyo making a request to a server in Virginia is dealing with 150ms of round-trip time just from physics, light can only travel so fast.
A CDN maintains Points of Presence (PoPs), edge servers scattered across major cities worldwide. The first request for a resource goes to the origin, but subsequent requests are served from whichever edge node is closest to the user.
Without CDN:
User (Tokyo) ──────────────────────────── Origin (Virginia)
~150ms RTT
With CDN:
User (Tokyo) ──── CDN Edge (Tokyo) ──── Origin (Virginia)
~5ms RTT (only on first request)Cache-Control headers
The Cache-Control header is how you tell CDNs (and browsers) how to cache your responses. Getting this right is the foundation of CDN performance.
# For versioned static assets (JS/CSS with content hash in filename)
# Cache for 1 year - the filename changes when content changes
Cache-Control: public, max-age=31536000, immutable
# For HTML pages - don't cache, or cache very briefly
Cache-Control: public, max-age=0, must-revalidate
# For API responses - don't cache at all
Cache-Control: no-store
# For user-specific data - only the browser caches, not CDN
Cache-Control: private, max-age=300| Directive | Meaning |
|---|---|
public | CDN and browser can cache |
private | Only browser can cache (not CDN) |
max-age=N | Cache for N seconds |
immutable | Content will never change; skip revalidation |
no-store | Never cache anywhere |
must-revalidate | Check with origin when stale |
s-maxage=N | CDN-specific max-age (overrides max-age for CDNs) |
Content-addressable URLs
The immutable asset pattern
The challenge with long cache TTLs is that you can't update the cached file. The solution is to include a hash of the file's contents in the filename. When the content changes, the hash changes, the URL changes, and the new file gets fetched automatically.
<!-- Without content hashing: cache for 5 minutes max, risk of stale CSS -->
<link rel="stylesheet" href="/styles/main.css">
<!-- With content hashing: cache for 1 year, guaranteed fresh on change -->
<link rel="stylesheet" href="/styles/main.a3f9c2b1.css">Modern build tools like Vite and webpack do this automatically. Your build output gets filenames like main.a3f9c2b1.js and a manifest file that maps logical names to hashed names.
Edge functions
Running code at the edge
Edge functions are serverlessWhat is serverless?A hosting model where individual functions run on demand and the platform handles all server management, scaling, and uptime for you. functions that run on the CDNWhat is cdn?Content Delivery Network - a network of servers around the world that caches your files and serves them from the location closest to the user, making pages load faster.'s own infrastructure, at the edge node nearest to the user. Because they don't need to reach your originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443. server, they can respond in single-digit milliseconds.
// Cloudflare Worker - runs at the edge, globally
export default {
async fetch(request, env) {
const url = new URL(request.url);
// A/B test routing at the edge - no origin round-trip needed
if (url.pathname === '/') {
const variant = Math.random() < 0.5 ? 'a' : 'b';
url.pathname = `/variants/${variant}/index.html`;
return fetch(url.toString());
}
// Redirect old URLs at the edge
if (url.pathname.startsWith('/blog/old-post')) {
return Response.redirect(
'https://example.com/blog/new-post', 301
);
}
return fetch(request);
}
};AuthenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. at the edge
One of the best uses of edge functions is validating sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request. tokens before the request ever hits your origin. This protects your origin from unauthenticated traffic entirely.
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Only protect API routes
if (url.pathname.startsWith('/api/')) {
const cookie = request.headers.get('Cookie') || '';
const sessionToken = parseCookie(cookie, 'session');
if (!sessionToken) {
return new Response(
JSON.stringify({ error: 'Unauthorized' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
// Optionally validate the token here before forwarding
}
return fetch(request);
}
};fetch, Request, Response, Headers, URL, crypto.Image optimization
Why images deserve special treatment
Images are typically 60–80% of a page's total byte weight. Even with perfect JavaScript bundling, unoptimized images will dominate your load time. The key levers are format, dimensions, and lazy loadingWhat is lazy loading?Deferring the loading of a resource like an image or component until the moment it's actually needed, speeding up the initial page load..
<!-- Bad: large JPEG served at full resolution to all devices -->
<img src="/hero.jpg" alt="Hero image">
<!-- Good: modern format, responsive sizes, lazy loading for below-fold -->
<img
src="/hero.webp"
srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="Hero image"
loading="lazy"
decoding="async"
>| Format | Best for | Typical savings vs. JPEG |
|---|---|---|
| WebP | Photos, general use | 25–35% smaller |
| AVIF | Photos where size matters most | 40–55% smaller |
| SVG | Icons, logos, illustrations | Infinitely scalable, tiny |
| PNG | Screenshots, images with transparency | Lossless |
Quick reference
| Optimization | Where to apply | Impact |
|---|---|---|
immutable + content hash | JS, CSS, fonts | Eliminates repeat downloads |
no-store for HTML | HTML pages | Always fresh, never stale |
| CDN for all static assets | Images, JS, CSS | Reduces latency globally |
| Edge auth middleware | Protected API routes | Faster rejection, less origin load |
| Responsive images + WebP | All <img> tags | 25–55% smaller images |
| Lazy loading | Below-fold images | Faster initial page load |