Most teams look at their CDN invoice and see a single egress line item, then spend months negotiating a lower per-gigabyte rate while ignoring the underlying behavior that drives that number. In my experience, the fastest path to cutting cdn cost is not a better discount, it is sending fewer bytes and making fewer origin trips in the first place. After managing edge delivery for high-traffic media and SaaS properties pushing 300+ Tbps at peak, I've found that three levers consistently move the needle on your bill: intelligent compression that reduces billable bytes at the edge, disciplined egress reduction through cache architecture, and request coalescing that prevents duplicate origin fetches under load. This article breaks down how each works in production and how to measure the dollar impact.
Deconstructing Your Egress Bill: Origin Fetches, Edge Egress, and Hidden Inter-Region Fees
CDN pricing looks simple until you see how providers itemize usage. You are typically billed for three distinct transfer types: edge egress to eyeballs, origin egress from your cloud provider to the CDN, and mid-tier or shield egress between CDN POPs. The edge egress rate gets all the attention, but origin and inter-region transfer often costs 2-4x more because cloud providers like AWS, GCP, and Azure charge $0.05-$0.09/GB for internet egress while CDN edge rates can be $0.02-$0.04/GB at scale. I audited a video platform last year where 38% of their total delivery spend was actually AWS origin egress to their CDN, not the CDN bill itself.
That split changes your optimization priorities. A 70% cache hit ratio sounds healthy until you realize the remaining 30% miss traffic is generating both a CDN origin-fetch charge and a cloud egress charge simultaneously. Every cache miss is double-billed. When you add an origin shield, you consolidate those misses to a single shield POP, which means your origin only serves one copy per region instead of one per edge POP. The trade-off is that you now pay for shield-to-edge transfer, but at internal CDN rates which are typically 60-80% cheaper than origin egress.
Mapping Cache Misses to Dollars
To quantify this, export your CDN logs and group requests by cache status (HIT, MISS, REVALIDATED, BYPASS) and multiply by origin response size. I've found that separating static assets from dynamic API traffic is critical here. Static misses are almost always fixable with TTL tuning, while API misses require a different strategy entirely. Your goal is to attribute cost per URL pattern, not just per POP. One e-commerce client discovered that a single uncached personalization endpoint (/api/recommendations) accounted for 22% of their origin egress despite being only 4% of requests, because responses averaged 85KB and had a 0% hit ratio.
Why Inter-POP Transfer Is Not Free
Multi-tier caching architectures introduce another line item that surprises teams: tiered or shield fetch fees. Fastly, CloudFront, Akamai and Cloudflare all handle this differently. Some include shield fetches in your edge egress allowance, others bill it as a separate tier at a reduced rate. Before enabling Origin Shield and Multi-Tier Caching: Reducing Origin Load at Scale, model the math: if your current origin egress is $8,000/month and shield reduces origin fetches by 85%, but adds $1,200 in shield transfer, your net saving is still $5,600. Without that model, shield looks like an added cost rather than an arbitrage opportunity.
Compressing Billable Bytes at the Edge: Brotli, Zstandard, and Image-Aware Encoding
Compression is the most direct form of egress reduction because you bill fewer bytes for every single hit, not just misses. Yet most configurations I review are still serving gzip at level 6 for everything, leaving 15-25% savings on the table. The modern edge supports content negotiation for Brotli (br) and Zstandard (zstd), and the CPU cost to compress at the edge is negligible compared to the bandwidth saved, especially when you pre-compress static assets.
Brotli at quality 10-11 outperforms gzip by 18-22% on text assets like JavaScript, CSS, and JSON. Zstandard, particularly with a shared dictionary for API responses, can outperform both by another 10-15% and decompresses 3-5x faster on mobile devices. For images and video, the win is larger but requires format negotiation. Serving AVIF or WebP instead of JPEG at the edge typically cuts image payload by 40-60% with no perceptual loss. I recently migrated a news site to on-the-fly AVIF conversion at the edge and cut total image egress by 47%, which translated to ~$14k/month in savings on a 500TB property.
# Nginx + CDN origin: Serve pre-compressed assets with correct Vary
# Build step generates .br and .zst variants for all static text assets
location ~* \.(js|css|json|svg|xml)$ {
gzip off; # Let CDN handle dynamic compression
brotli_static on;
zstd_static on;
# Critical: Vary header ensures cache keys include encoding
add_header Vary "Accept-Encoding" always;
add_header Cache-Control "public, max-age=31536000, immutable";
# Only serve pre-compressed if client supports it
# CDN will normalize Accept-Encoding to br, zstd, or gzip
}
Implementing Encoding Negotiation Without Cache Fragmentation
The mistake I see repeatedly is enabling Brotli at the CDN without fixing the Vary header or cache key. If your origin returns Vary: Accept-Encoding inconsistently, you end up caching three separate copies of every asset (br/zstd/gzip) and worse, you can poison the cache by serving a Brotli-encoded object to a client that only supports gzip. The correct pattern is to normalize Accept-Encoding at the edge to a small set of values before the cache lookup, as described in the HTTP Working Group Specifications on content negotiation.
In practice, configure your CDN to do this mapping: if the request contains zstd, normalize to zstd; else if it contains br, normalize to br; otherwise gzip. Cache on that normalized value, not the raw header. For origins, always pre-compress rather than relying on on-the-fly compression. On-the-fly Brotli q11 can take 80-120ms per asset and will spike origin CPU under coalesced load. Pre-building at deploy time shifts that cost to CI and keeps TTFB flat.
| Compression Strategy | Typical Egress Saving vs. gzip | CPU Cost at Edge | Ideal Use Case |
|---|---|---|---|
| Brotli q11 (static) | 18-22% on JS/CSS/JSON | Low (pre-compressed) | Static text assets > 1KB |
| Zstandard + Dictionary | 28-35% on JSON/API | Very Low | API responses with repetitive schemas |
| AVIF / WebP Negotiation | 40-60% on Images | Medium (image transcoding) | User-generated and editorial images |
| Gzip 6 (baseline) | Baseline | Low | Fallback for legacy clients |
| Dynamic Brotli q4-5 | 12-15% on HTML/API | Medium | Uncacheable HTML where pre-compress is impossible |
One final detail: do not compress already-compressed formats. I've seen configs that double-compress JPEG, PNG, WOFF2 and MP4, adding CPU overhead and sometimes increasing byte size due to headers. Your edge rule should bypass compression for content-type: image/*, video/*, font/woff2, application/octet-stream unless you are transcoding to a more efficient codec.
Request Coalescing and Collapsed Forwarding: Stopping Duplicate Origin Fetches
Even with a 95% hit ratio, the remaining 5% can bankrupt your origin budget if those misses arrive simultaneously. This is the thundering herd problem: when a popular object expires or is purged, 500 edge servers can request the same object from origin at once, and you pay egress for all 500 fetches. Request coalescing, also called collapsed forwarding or request collapsing, solves this by ensuring only one request goes to origin while the rest wait for the response.
Without coalescing, a cache purge on a homepage JSON file during a traffic spike can generate a 40x origin amplification. With coalescing enabled, that same purge results in exactly one origin fetch. In my experience managing flash-sale events, enabling collapsed forwarding reduced origin egress during purge windows by 92% and cut origin p95 latency in half because the origin was no longer contending with duplicate work.
// Example: Fastly VCL / Varnish-style collapsed forwarding logic
// Also applies to Cloudflare Tiered Cache and Akamai SureRoute
sub vcl_miss {
// Enable request coalescing for this object
// All concurrent misses for same cache key will wait
if (req.url ~ "^/api/catalog" || req.url ~ "\.(js|css|json)$") {
// set coalescing window - how long to wait for origin response
set req.backend_wait_limit = 30s;
// Stale-while-revalidate interaction: serve stale while coalesced fetch completes
if (stale.exists) {
return (deliver_stale);
}
}
}
# Nginx equivalent for origin shield layer
# proxy_cache_lock collapses concurrent misses into one origin fetch
proxy_cache_lock on;
proxy_cache_lock_timeout 10s;
proxy_cache_lock_age 5s;
proxy_cache_use_stale updating error timeout http_500;
Tuning Coalescing Timeouts and Stale Fallbacks
Coalescing is not free. Waiting requests consume edge worker memory and connections, so you need to tune timeouts to your origin latency. If your origin p95 is 400ms, a 10-second lock timeout is generous without holding connections too long. More importantly, pair coalescing with stale-while-revalidate. That way, if the origin fetch stalls, waiting clients still get a slightly stale object instead of a 503. For a deeper look at this interaction, see Stale-While-Revalidate: The CDN Pattern That Eliminates Cache Misses.
I also recommend different coalescing policies by content class. For immutable static assets with long TTLs, enable aggressive coalescing – there is no downside to waiting 5 seconds for the single fresh copy. For low-TTL API responses, keep the coalescing window short (1-2 seconds) and always allow stale delivery. This prevents a slow API origin from propagating latency to every edge POP.
Measuring Coalescing Effectiveness
Most CDNs expose a metric like origin_requests_collapsed or shield_collapsed_requests. Track the ratio of collapsed requests to total misses. A healthy setup for high-cardinality content should show 3:1 to 10:1 collapse ratios during peak. If your ratio is near 1:1, your cache key is too granular (perhaps including a user ID or query param that should be stripped) or your coalescing is not enabled for that path. Fixing cache key granularity alone once improved a client's coalescing ratio from 1.2:1 to 8:1, cutting origin egress by $6,200/month without changing TTLs.
Architecting Multi-Tier Caching to Arbitrage Origin Egress Costs
Origin shield is often sold as a performance feature, but I evaluate it primarily as a cost arbitrage tool. The economics are straightforward: you pay a lower rate to fetch from shield than to fetch from origin, so adding a tier is profitable if the reduction in origin fetches outweighs the shield fee. For properties under 50TB/month, the math may not justify it. Above 200TB/month with a hit ratio below 85%, shield almost always pays for itself within the first billing cycle.
The key is placement and fan-out. A single global shield POP minimizes origin fetches but adds latency for distant edges. A regional shield (e.g., US-East, EU-West, APAC) balances latency and cost, with each region collapsing to its nearest shield. I've found that two to three regional shields capture 80-90% of the benefit of a full multi-tier mesh without the complexity of managing tiered cache keys across 10+ layers. Your CDN provider can usually show you an origin request map by edge POP to identify the optimal shield locations.
Cache Key Consistency Across Tiers
Tiered caching only collapses requests if all tiers share the same cache key. I've debugged cases where edge POPs stripped query params but the shield did not, causing the shield to treat /product?id=123&utm_source=ad and /product?id=123 as different objects and forwarding both to origin. Normalize your cache key at the outermost tier: sort query params, strip marketing params, lowercase the path, and normalize the encoding header before the key is calculated. Document this logic once and enforce it in both edge and shield configs; divergence is the most common source of shield ineffectiveness.
This is where TTL strategy and shielding intersect. A long TTL on the shield (e.g., 24h) with a shorter TTL at the edge (e.g., 5m) lets you purge the edge quickly while shield continues to serve and absorb origin load. When you need to invalidate, purge edge only and let shield revalidate in the background. This pattern, which I detail in CDN Caching Strategies: TTL, Cache-Control Headers and Invalidation Patterns, reduces origin load during purge storms by an order of magnitude.
Controlling Egress for Uncacheable Content: APIs, Personalization, and Prefetch
Static assets are easy to optimize. The hard egress dollars are in dynamic, uncacheable traffic: authenticated APIs, personalized HTML, and POST requests. You cannot cache these in the traditional sense, but you can still reduce egress with three techniques: response collapsing for identical API calls, edge prefetch, and compute short-circuiting.
Even personalized APIs often have significant duplication. Ten thousand users requesting /api/config?app_version=2.4 within a minute will get the same response if you exclude the Authorization header from the cache key and use Vary correctly, or by caching with a short 5-10 second TTL (micro-caching). I deployed 2-second micro-caching on a leaderboard API that was thought to be uncacheable and cut origin egress by 71% with no visible staleness to users, because the leaderboard only updated every 5 seconds anyway.
// Cloudflare Worker / Fastly Compute@Edge: Short-circuit and coalesce API egress
// Micro-cache authenticated responses for 5s, collapse duplicates
async function handleRequest(request, ctx) {
const url = new URL(request.url);
// Only micro-cache safe GET APIs, bypass POST/auth
if (request.method === "GET" && url.pathname.startsWith("/api/catalog")) {
const cacheKey = new Request(url.origin + url.pathname + url.search, request);
const cache = caches.default;
let response = await cache.match(cacheKey);
if (response) {
response.headers.set("X-Cache", "HIT-micro");
return response;
}
// Coalesced fetch: edge will deduplicate concurrent fetches
response = await fetch(request, { cf: { cacheTtl: 5 }});
// Only cache successful, cacheable responses
if (response.ok && response.headers.get("Cache-Control") !== "private") {
response = new Response(response.body, response);
response.headers.set("Cache-Control", "public, max-age=5, stale-while-revalidate=30");
ctx.waitUntil(cache.put(cacheKey, response.clone()));
}
return response;
}
return fetch(request);
}
Prefetch is the second lever. Instead of letting the client discover 30 assets via HTML parsing and then requesting them serially, use 103 Early Hints or Link headers to push the CDN to prefetch critical assets into edge cache before the client asks. This warms the cache proactively and converts what would have been 30 individual misses into a shield-coalesced warm-up.
Finally, edge compute can eliminate origin egress entirely for some requests. Validating JWTs, normalizing parameters, serving synthetic error responses, or returning cached stale data during origin outages can all be done at the edge without ever contacting origin. Every request you short-circuit is a 100% egress saving for that request.
Attributing Cost to Cache Efficiency: Metrics That Actually Map to Your Invoice
Hit ratio alone does not explain your bill. Two sites with 90% hit ratios can have wildly different egress costs if one serves 10KB JSON and the other serves 4MB video. I track four metrics that correlate directly to spend: origin egress GB per million requests, weighted hit ratio by bytes (not requests), collapsed request ratio, and compression ratio at edge.
Weighted hit ratio by bytes is the most important. Calculate it as bytes_served_from_edge / total_bytes_served. A site that hits 95% of requests but misses on the largest 5% of objects can have a byte hit ratio of only 70%, meaning 30% of your bill is still origin-driven. Segment this by content type and by POP. I maintain a dashboard that shows byte hit ratio per URL pattern and per shield region, which immediately highlights where a TTL adjustment or shield change will have the most dollar impact.
For forecasting, build a simple model: Projected Egress Cost = (Total Edge GB * (1 - Byte Hit Ratio) * Origin Egress Rate) + (Total Edge GB * Byte Hit Ratio * Edge Egress Rate). Plug in your target hit ratio and compression savings to see the saving. When we increased byte hit ratio from 82% to 94% and enabled Brotli at the edge for a SaaS customer, this model predicted $38k/month savings; actual was $41k. The model is also useful for justifying engineering time: if a week of work on request coalescing saves $12k/month, the payback period is obvious.
For ongoing validation, rely on the cache status headers and billing dimensions defined by your provider and cross-check with origin logs. The web.dev Articles on content efficiency provide excellent reference points for compression benchmarks, while the IETF specifications on Cache-Control help you avoid subtle misconfigurations that silently disable caching.
Frequently Asked Questions
Does enabling origin shield always reduce total cdn cost?
No. Shield adds a tier whose transfer is billed at a lower rate than origin egress, so it only saves money if it collapses enough origin fetches to offset its own fee. For low-traffic sites under 50TB/month or those with already-high 95%+ byte hit ratios, the saving may be marginal. For high-traffic properties with geographic miss fan-out, I typically see 60-85% reductions in origin egress after enabling regional shields, which easily outweighs the shield cost. Model your current origin GB vs. projected shield GB before enabling.
How do Brotli and Zstandard affect cache hit ratio and storage?
They increase storage slightly because you cache multiple variants (gzip, Brotli, Zstandard) per object, but each variant is smaller and total edge storage is rarely a limiting factor. The bigger impact is on cache key design. You must normalize Accept-Encoding and include it in the cache key via Vary, otherwise you risk serving the wrong encoding. When done correctly, hit ratio is unchanged and byte egress drops by 15-35% depending on content mix. I recommend pre-compressing static assets at build time to avoid origin CPU spikes.
What is the difference between request coalescing and stale-while-revalidate?
Request coalescing (collapsed forwarding) deduplicates concurrent misses so only one request reaches origin while others wait. Stale-while-revalidate allows the edge to serve a stale cached object immediately while revalidating in the background. They complement each other: coalescing protects origin from thundering herds, while stale-while-revalidate protects users from latency during that single origin fetch. Used together, you get one origin fetch per expiration window and zero user-visible misses.
Can I use request coalescing for authenticated or personalized responses?
Generally no, unless you can safely share the response. If your cache key includes Authorization or Cookie, each user gets a distinct key and there is nothing to coalesce. However, if personalization is limited to a header you can strip or you use short micro-caching for semi-public APIs (like configs or catalogs), you can coalesce effectively. For truly private responses, focus on edge compute short-circuiting and connection reuse rather than collapsing origin fetches.