CDN Optimization Techniques - Performance at Scale
LESSON
CDN Optimization Techniques - Performance at Scale
By the end of this lesson, you will be able to...
Choose a CDN metric that corresponds to a specific user or origin bottleneck.
Trace how key normalization and a shield layer change the cost of a campaign-time cache miss.
Reject an apparent hit-rate improvement when it fragments a cache, hides an unsafe response, or leaves the expensive origin path unchanged.
Idea in one sentence: CDN optimization works when it makes the right reusable response cheap to serve and the remaining misses cheap to refill—not when it merely raises one aggregate cache number.
Core Insight
For Atlas Shop, a global campaign launches. The CDN dashboard reports an encouraging 94% object hit ratio. Yet the catalog origin's CPU remains high, p99 page latency is poor, and the rendered homepage is still the most expensive route.
The first explanation is simple: 94% is high, so caching must be working. It would be a good conclusion if the 94% represented the costly responses. But the hit ratio is dominated by tiny icons and a long-lived logo. The public homepage, which triggers an expensive render on a miss, is fragmented by tracking query strings and an unnecessary device header. It misses often enough to keep the origin busy.
The correction is to optimize a request path, not a dashboard total. Ask which responses consume origin work, bytes, or tail latency; make only truly equivalent requests share one cache key; then arrange refill so many outer-edge misses do not become many origin fetches.
The Production Symptom: A Good Percentage and a Bad Page
The team examines one minute of illustrative campaign traffic:
| Request class | Requests | Edge hits | Origin work per miss | User consequence |
|---|---|---|---|---|
/assets/logo.svg |
80,000 | 99.9% | tiny file read | Almost no effect on page p99. |
/assets/app.8f3.js |
15,000 | 99.5% | small asset fetch | Healthy reuse. |
/home?utm_source=... |
5,000 | 45% | 180 ms render + catalog reads | Dominates origin CPU and p99. |
The aggregate number is excellent because the first two rows are numerous. The operational pressure is in the last row. A cache metric becomes useful only when it is broken down by route or object class and paired with origin cost and user latency.
The page has two public representations, Spanish and English. But utm_source, utm_campaign, and a device header that does not change the HTML have been included in its cache key. The cache stores many copies that are semantically identical:
home | es | utm=mail | device=A -> same HTML
home | es | utm=social | device=B -> same HTML
That is not safe personalization. It is accidental fragmentation.
The Initial Model: Increase TTL Until Hits Rise
Longer freshness can reduce misses, so increasing the TTL is a reasonable first lever when a public response changes rarely and a named stale window is acceptable. It works in that narrow setting.
It fails as a general optimization rule. A longer TTL does not merge duplicate keys. It does not make a personalized response shareable. It does not protect an origin from a sudden purge. And it may hide a product freshness requirement by serving an old response for longer.
The parallel mistake is to add every request field to the key “for safety.” That prevents an incorrect shared hit, but it also treats every harmless difference as a different object. Good key design is neither maximal sharing nor maximal separation. It names exactly the dimensions that change the representation.
The Better Model: Optimize the Bottleneck, Then the Miss Path
In plain English, ask two questions in order:
- Which route or object class makes users slow or makes origin expensive?
- When that class misses, how many requests and bytes reach origin before a warm copy exists again?
In this scenario, the homepage's language changes its text, so language belongs in the key. Tracking parameters do not change the public HTML, so they can be normalized away before cache lookup if analytics preserves them elsewhere. The device header belongs only if the origin actually emits a different representation because of it.
The technical mechanisms are key normalization, tiered caching or shielding, and request coalescing.
- Key normalization maps equivalent requests to one safe cache entry.
- A shield or upper cache tier lets many outer edge locations reuse a fill before reaching the origin.
- Request coalescing lets concurrent misses for the same key wait for one fetch instead of creating duplicate origin work.
Cloudflare's cache-key documentation makes the relevant boundary explicit: cache configuration includes a cache key and purge modes that must match it. Cloudflare cache-key documentation. CloudFront documents Origin Shield as an additional cache layer that can consolidate same-object origin requests, while also adding cost and being a poor fit for some low-cacheability or proxy-only traffic. AWS Origin Shield documentation.
These provider examples support a general lesson, not a one-size-fits-all configuration: a second layer is valuable only when enough requests can safely reuse the same object to offset its extra hop, cost, and operational complexity.
A Worked Trace: Make a Homepage Miss Bounded
First, change the public homepage key:
before: home | language | device-header | all query parameters
after: home | language
The after key is safe only under an explicit assumption: the HTML does not vary by device header or tracking parameters. Verify that assumption with origin output tests before deployment. If the page truly varies by an input, keep that input as a bounded key dimension or choose a different response boundary.
Now trace a Spanish campaign request after a purge. The timestamps are illustrative.
| Step | Outer edge | Shield / upper tier | Origin | Result |
|---|---|---|---|---|
| 1 | Request home|es misses after a purge. |
Also misses; begins one fetch. | Receives one render request. | First reader waits for the fill. |
| 2 | Twenty other edges miss home|es. |
Sees the same in-flight fill. | Does not receive twenty more equivalent renders. | Followers are coalesced or receive the cached fill once ready. |
| 3 | Origin returns the Spanish public HTML. | Stores the object. | Render completes once. | Outer edges receive and store the same result. |
| 4 | Later Spanish request with another utm_source. |
Not involved. | Not involved. | Normalized key hits at the nearby edge. |
Before normalization, those twenty edges might have used several equivalent keys, making a shield less effective because it sees several different objects. Before coalescing, even a well-chosen key can produce duplicate work when requests arrive concurrently. Optimization is therefore a chain: correct key, cacheable response, bounded refill, then measurement.
So far, the plan has made a costly public homepage more reusable and protected its source on a miss. It has not made the cache safe for a signed-in basket, proved that the origin is now fast, or eliminated the need for purge and revalidation policies.
What to Measure Before Calling It Better
Compare the same request class before and after, under a representative campaign slice. Use a baseline; do not call a quiet five-minute period an optimization result.
| Question | Evidence |
|---|---|
| Did reusable homepages share safely? | Key cardinality by language, response-version samples, wrong-language reports. |
| Did the expensive path reach origin less often? | Homepage origin render count, origin CPU, and shield/outer cache status. |
| Did misses become less explosive? | Concurrent same-key fetches, shield hit rate, post-purge origin peak. |
| Did users improve? | Homepage p50/p95/p99 and error rate by region. |
| Did bytes improve where bytes are the cost? | Compressed response size and byte hit ratio by object class. |
Compression belongs in this same evidence loop. Compressing a large public text response can reduce transferred bytes even when the number of objects is unchanged. It costs CPU somewhere in the delivery path and may not help already-compressed images. Measure response sizes and CPU, rather than enabling compression as a universal slogan.
Trade-offs and Limits
This optimization improves reuse and origin protection for public homepages. It costs a more explicit cache contract, extra routing or shield configuration, observability work, and potentially an additional service charge or hop.
The trade-off is situated:
- A broad key is useful only when all merged requests can safely receive the same representation.
- A longer TTL is useful only when its stale window matches the product promise.
- A shield is useful when many locations request the same cacheable object; it is less useful for infrequent, low-cacheability, or private traffic.
- Compression is useful when the bytes saved matter more than the compute and latency it adds.
This does not solve a slow database query inside every remaining origin miss. It only makes repeated work less likely to reach that query. The next lesson uses profiling to inspect the origin path once the request-level evidence has identified that it is still worth investigating.
The boundary signal is a mismatch between the intended mechanism and the evidence: a higher aggregate hit ratio with unchanged homepage p99, a shield layer with little reuse, a sudden rise in key cardinality, or a cache hit that serves the wrong representation. Treat each as a reason to revisit the response contract, not as a prompt to add more arbitrary knobs.
Check: A key-normalization change raises the homepage hit ratio from 45% to 88%, but some French visitors now receive Spanish text. Did the change succeed?
Think first, then reveal.
Answer: No. The change merged requests that were not equivalent. The response correctness failure outweighs the hit-rate gain. Restore language as a key dimension, verify output by variant, and then measure reuse inside each safe language population.
Practice: Choose the Next Lever
The campaign has these observations:
/homeis public and costly; it varies only by language.- Query parameters are for analytics only.
- After a tag purge, 60 outer locations request the English homepage in one second.
- The origin can handle five simultaneous homepage renders.
- Images are already compressed and mostly cache hits.
Propose the smallest next optimization. Name one key change, one miss-path control, one metric that should fall, and one correctness check that must remain green.
A good answer should mention:
- a key of
home|languagewith analytics parameters removed only after confirming they do not change HTML; - shielding or request coalescing so 60 equivalent misses do not become 60 origin renders;
- homepage origin-render count, concurrent same-key fetches, or p99 as the target signal rather than the aggregate asset hit ratio; and
- response-version or language-content checks to ensure the new sharing boundary is safe.
Connections
The previous lesson made purge a transition problem: correctness and refill stability must both hold. This lesson reduces the cost of that transition by shaping reusable keys and consolidating duplicate fills. The next lesson moves inside the origin process: when a measured miss still dominates, a profile can show whether CPU, allocation, locks, or waiting are responsible.
Resources
- [DOCS] Use Amazon CloudFront Origin Shield — Focus: Inspect duplicate-request consolidation, origin-load reduction, cost, and cases where the extra layer is not a good fit.
- [DOCS] Cloudflare cache keys — Focus: Relate safe request equivalence to cache keys and granular invalidation handles.
- [ARTICLE] HTTP caching — Focus: Revisit
Vary, freshness, and conditional validation before merging request classes.
Key Takeaways
- Optimize CDN behavior by route and object class: aggregate hit ratio can conceal an expensive, low-hit origin path.
- Safe key normalization creates reuse; arbitrary variation destroys it, and unsafe normalization creates wrong responses.
- Shields and coalescing make a cache miss less likely to become many duplicate origin renders.
- TTL, key breadth, shielding, and compression all have a trade-off that must match a named freshness, correctness, or cost constraint.
- Verify cache status, key cardinality, origin work, response correctness, bytes, and tail latency together before declaring an optimization successful.
← Back to Caching, Workers, and Performance