Problem Users far from your servers see slow responses, and traffic spikes or attacks hit the origin directly. This guide answers whether and how to put a CDN or edge compute in front of a service.
CDNs cache content in hundreds of PoPs near users, cutting latency and egress bills; edge compute runs small code there. Bandwidth price spreads 10x between providers.
A CDN terminates TLS close to the user, serves cached responses from its PoP, and fetches misses from your origin over warm, long-lived connections. It turns a 150 ms intercontinental round trip into 10-30 ms, absorbs traffic spikes and DDoS, and, because CDN bandwidth is cheaper than cloud egress, often cuts the bill. Edge compute (Workers, Compute@Edge, Lambda@Edge) adds logic at the same place.
Key facts
CDN bandwidth (2026-ish): CloudFront order of $0.085/GB in NA/EU for the first 10 TB (1 TB/month free); Bunny order of $0.005-0.01/GB; Cloudflare plans don't bill bandwidth.
Origin → CDN traffic from AWS to CloudFront is free; from S3 to Cloudflare it is normal S3 egress (use R2).
A good static-asset hit ratio is 90-99 %; APIs reach 0-50 % unless designed for caching.
TLS 1.3 + HTTP/3 at the edge saves 1-2 RTTs per new connection.
Use when
Users are spread geographically, content is cacheable (assets, media, downloads, public API GETs), or you need DDoS/WAF protection.
Avoid when
Traffic is private, uncacheable and from one region; a CDN then adds a hop and a bill line.
Deep dive
Provider map
Provider
Strength
Pricing shape
Cloudflare
Free/flat bandwidth, Workers, R2, WAF, huge PoP count
Per plan + per feature; Enterprise for big media
CloudFront
Free origin pull from AWS, Lambda@Edge, AWS integration
Per GB + per request; flat-rate plans since 2025
Fastly
Instant purge (~150 ms), VCL/Compute, fine-grained control
Per GB, minimums
Akamai
Largest footprint, enterprise media
Contract
Bunny CDN
Cheapest per GB, simple, storage + video
Per GB by region
Google Cloud CDN / Azure Front Door
In-cloud integration
Per GB + requests
Decision table
Situation
Choice
Why
Static site, SPA, assets
Cloudflare Pages/R2 or CloudFront + S3
Cheap, global, cache everything with hashed filenames
Video / game patches / large downloads, many TB
Bunny or Cloudflare (check ToS for large video on non-Enterprise plans) with R2/B2 origin
Per-GB price decides
AWS-hosted origin, moderate traffic
CloudFront
Free origin pull, IAM-integrated OAC
Need instant purge of personalized-ish content
Fastly
Purge in ~150 ms, surrogate keys
Edge auth, A/B, geo routing, image resizing
Workers / Compute@Edge
Logic before the origin
DDoS-prone (games, crypto, politics)
Cloudflare or Akamai
Capacity in Tbit/s
Live WebSockets / game UDP
CDN only for HTTP; UDP via Spectrum-style proxies or direct
Most CDNs proxy only HTTP(S)/WebSocket
Code
Cache headers decide everything. Hashed assets cache forever; HTML revalidates; APIs use short TTLs with stale serving:
location ~* \.(js|css|woff2|png|webp|avif)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location = /index.html {
add_header Cache-Control "public, max-age=0, must-revalidate";
}
location /api/leaderboard {
# CDN keeps it 30 s, serves stale for 5 min while refetching or if the origin is down
add_header Cache-Control "public, s-maxage=30, stale-while-revalidate=300, stale-if-error=86400";
add_header Vary "Accept-Encoding";
proxy_pass http://app;
}
Lock the origin so only the CDN can reach it (Cloudflare ranges example; or use mTLS / a secret header):
for cidr in $(curl -s https://www.cloudflare.com/ips-v4); do
nft add rule inet filter input ip saddr $cidr tcp dport 443 accept
done
nft add rule inet filter input tcp dport 443 drop
Efficiency
Cost: a hit costs the PoP a cache lookup and a send; a miss adds one origin fetch. The bill is GB served times the per-GB price plus requests, and latency is bound by the client-to-PoP round trip. Cloudflare Workers bill $0.30 per additional million requests and $0.02 per additional million CPU ms above the included amounts, with no charge for duration (Workers pricing, as of 2026-10).
Levers:
long TTL with content-hashed names (immutable) — hit ratio toward the 90-99 % in Key facts (order of) — static assets
tiered cache / origin shield — lower tiers ask an upper tier before the origin, so fewer origin requests (Cloudflare Tiered Cache docs) — many PoPs, one origin
stale-while-revalidate and stale-if-error — hide origin latency and outages (RFC 5861) — APIs, leaderboards
no Set-Cookie, no private on cacheable routes — Cloudflare does not cache responses that have them (Cloudflare default cache behavior) — cacheable routes
zero-egress origin such as R2 — no egress bandwidth charge (R2 pricing, as of 2026-10) — origin on Cloudflare
Naive → standard → state of the art:
no CDN · every request pays the full RTT and origin egress
CDN with TTLs and hashed filenames · hits from the nearest PoP, the origin sees misses only
tiered cache, stale serving, purge by tag · about one origin request per object per TTL (order of)
Implementation notes
Cache key = URL + selected headers. Strip tracking query params and avoid Vary: User-Agent, or the hit ratio collapses.
Content-hash filenames (app.3f9c1a.js) make purges unnecessary for assets.
Origin shield / tiered cache: one regional PoP fetches from the origin for all others; cuts origin load 5-20x.
Compress at the edge (Brotli for text); serve AVIF/WebP via image resizing services.
Measure with response headers (cf-cache-status, x-cache, age) and keep a hit-ratio dashboard.
Pitfalls
Set-Cookie on a response makes most CDNs refuse to cache it.
Caching authenticated responses by accident (missing private) leaks one user's data to another.
Cloudflare's self-serve plans restrict serving mostly video or large binaries outside their media products; read the service terms before moving TBs.
Edge functions with per-request origin fetches can be slower than the origin alone if the origin is far and uncached.
Purge-everything on deploy causes an origin stampede; purge by tag/path.