Last year I was pulled into an architecture review for a retailer doing over $2M a day online. Their CDN had been “set and forget” since 2019, and on paper everything looked green. Then we plotted cache hit ratio by content type and found their product images — 68% of their bandwidth — were bypassing the cache entirely due to a single misconfigured query string rule. They’d been paying to serve their heaviest assets straight from origin for years without anyone noticing. That’s why a proper CDN assessment isn’t about dashboards; it’s about finding what’s silently expensive.
Mapping What You've Actually Deployed vs. What You Think You Deployed
I’ve never audited a CDN setup that matched its documentation. Not once in ten years. Teams inherit rules, someone adds a bypass for a campaign, a vendor migrates you to a new edge logic engine and doesn’t port that one weird header manipulation correctly. Over time you end up with layers of intent that no longer match reality. The first job in any optimization audit isn’t tuning — it’s inventory.
Start by pulling your live edge configuration and comparing it against your origin behavior. Don’t trust the portal UI alone. I always pull real response headers from multiple edge POPs and from origin directly. You’re looking for discrepancies in Cache-Control, Vary, Set-Cookie, and Surrogate-Control. I worked with a SaaS platform that thought they had a 24-hour TTL on their marketing site, but their origin was sending Cache-Control: private, max-age=0 on every response because a WordPress plugin was overriding it. The CDN was correctly respecting origin and not caching anything. We wouldn’t have caught that without checking both sides.
If you’re planning to fix what you find, or if those findings push you toward a move, you’ll want your remediation steps tracked the same way you’d track a migration. I keep findings mapped to the same structure we use in our Comprehensive CDN Migration Checklist: Every Step from Planning to Cutover so nothing falls through the cracks between “audit finding” and “production change.” It keeps engineering, product, and vendors aligned on what’s actually being changed and why.
# Audit script I use to compare edge vs. origin cache behavior
# Run from a test host that can resolve both edge and origin directly
echo "=== Checking Edge ==="
for url in "https://www.example.com/product/123" "https://www.example.com/static/app.js" "https://www.example.com/api/user"; do
echo ">> $url"
curl -s -I -H "Pragma: no-cache" "$url" | grep -i -E "^(HTTP|cache-control|age|x-cache|vary|set-cookie)"
echo ""
done
echo "=== Checking Origin Direct (bypass CDN) ==="
for url in "https://origin.example.com/product/123" "https://origin.example.com/static/app.js"; do
echo ">> $url"
curl -s -I --resolve origin.example.com:443:203.0.113.10 "$url" | grep -i -E "^(HTTP|cache-control|age|vary|set-cookie)"
echo ""
done
# What to look for:
# 1. Age: 0 or missing on cacheable assets = not caching
# 2. Vary: * or Vary: Cookie on static assets = cache fragmentation
# 3. Set-Cookie on static assets = cache bypass in most default configs
Finding the Bottlenecks That Don't Show Up on Status Pages
Once you know what you’ve deployed, you need to understand where it’s hurting you. Most teams look at average cache hit ratio and call it a day. That number is almost useless on its own. I’ve seen sites with a 92% hit ratio that still had terrible performance because the 8% that missed was the HTML document for every single page view. You’ve got to slice by content type, by path pattern, and by cache status.
The real bottlenecks usually fall into three buckets. First is origin shielding and tiering — or the lack of it. If every POP is going back to origin independently, you’re not really offloading anything during a traffic spike. Second is request collapsing and stale-while-revalidate handling. Without it, a cache purge or a popular piece of content expiring can cause a thundering herd that knocks your origin over. The specs in the HTTP Working Group Specifications around stale-while-revalidate and stale-if-error are worth reading closely here; most CDNs implement them but they’re not enabled by default. Third is TLS and connection reuse. I still find setups doing full TLS handshakes back to origin for every edge fetch because keep-alives were disabled years ago for debugging and never re-enabled.
These architectural choices directly impact who you should work with next. If your audit shows you need better Tier-1 shielding in APAC or stronger edge compute for personalization, that should shape your scoring far more than list price. When we formalize those requirements, we score them using the framework in our CDN Vendor Evaluation Matrix: Scoring Providers on What Actually Matters — it forces you to weight what your audit actually found instead of what looks good in a sales deck. And if you do go to market, don’t let vendors guess what you need; put these specific gaps into your CDN RFP Template: Writing Requirements That Get Meaningful Vendor Responses so you get answers you can compare.
// Example: VCL / Edge logic to handle thundering herd and serve stale
// This pattern has saved more origins during flash sales than any amount of scaling
sub vcl_hit {
if (obj.ttl >= 0s) {
// Normal hit
return (deliver);
}
// Object is stale - serve it while we revalidate in background
if (obj.ttl + obj.grace > 0s) {
// Use stale-while-revalidate: serve stale for 60s, refresh async
if (std.healthy(req.backend_hint)) {
set req.http.Grace = "stale-while-revalidate";
} else {
// Origin is sick - serve stale for longer with stale-if-error
set req.http.Grace = "stale-if-error";
}
return (deliver);
}
return (fetch);
}
sub vcl_fetch {
// Allow serving stale for 1 hour if origin fails
set beresp.grace = 1h;
// Keep edge connection to origin alive
set beresp.http.Connection = "keep-alive";
// Don't cache Set-Cookie responses for static assets
if (beresp.http.Set-Cookie && req.url ~ "^/static/") {
unset beresp.http.Set-Cookie;
}
}
Benchmarking Your Current Performance With Numbers That Mean Something
You can’t optimize what you haven’t measured, and vanity metrics will lie to you. I don’t care that your CDN reports “99.99% uptime” if your origin is doing twice the work it should. For a meaningful infrastructure review, you need to pull at least 30 days of logs from both edge and origin, then join them on URL and timestamp. Guidance from web.dev Articles on Core Web Vitals and TTFB is useful for framing user impact, but your internal efficiency metrics tell you where the architecture is leaking.
Here’s the set of metrics I benchmark on every assessment before recommending any changes. If you don’t have these, get your logging fixed before you change anything else. I ask the CDN for raw logs, not aggregated analytics, because I want to see cache status (HIT, MISS, REVALIDATED, BYPASS), edge POP, and origin response time for every request. Without that, you’re just guessing.
Run this benchmark for both peak and off-peak periods. A setup that looks efficient at 2 AM can fall apart at 2 PM when cache fragmentation from cookies or query strings starts to multiply. That’s also where you’ll spot the cost opportunities — every percentage point of hit ratio you gain on high-bandwidth assets is a direct reduction in origin egress and compute.
| Metric to Audit | Healthy Range (Intermediate Site) | Common Red Flag | What It Costs You |
|---|---|---|---|
| Overall Cache Hit Ratio | 88% - 96% | < 82% with static assets > 50% of traffic | Excess origin egress + slower TTFB |
| Origin Offload (Bandwidth % served from edge) | 85% - 95% | < 70% offload | Direct bandwidth and compute overage charges |
| TTFB p95 (Edge HIT vs. MISS) | HIT: < 80ms, MISS: < 450ms | HIT > 150ms or MISS > 800ms | Poor LCP, lower conversion rate |
| Stale / Revalidated Requests | 2% - 8% of total | < 0.5% (means grace/revalidation not enabled) | Origin floods on purge or expiry |
| TLS Handshake Time to Origin | < 40ms with keep-alive reused | > 120ms or 1:1 handshake-to-request ratio | Wasted latency and CPU on every miss |
Turning Audit Insights Into Prioritized Fixes That Actually Move the Needle
You've got the map and the metrics — now comes the hard part: deciding what to fix first. Don't fall into the trap of trying to boil the ocean. We've seen teams spend three months perfecting edge logic while ignoring a 62% origin offload rate that's costing them $18k a month in egress. The rule we use is simple: sort by impact on origin and p95, not by what's easiest to deploy.
Start with your cache key and TTL strategy. That's where you'll find the fastest wins. If your hit ratio is sitting at 78-82%, you're leaving massive performance on the table. A well-tuned setup for static and semi-dynamic content should be pushing 92-96% at the edge, with another 2-3% picked up at the shield/POP layer. Look for query string noise — things like utm_source, fbclid, or session IDs busting your cache 40% of the time. Normalizing those at the edge with a single rule can lift your hit ratio by 10-15 points overnight without touching a line of origin code. Next, tackle origin shielding and stale-while-revalidate. If you don't have shielding enabled, you're fanning every miss out to origin from 300+ PoPs instead of one.
Once those are solid, validate any bigger changes before you commit. You don't want to re-architect on a hunch. That's where a structured CDN Proof of Concept Planning: Designing a PoC That Answers Real Questions pays for itself — you'll test that new cache tier or Argo-style routing against real traffic patterns instead of synthetic lab tests. Keep your PoC tightly scoped: one geography, 5-10% of traffic, and a clear rollback trigger if p95 TTFB degrades by more than 20ms.
# VCL / Edge Worker snippet: normalize cache key and serve stale
# Don't cache-bust on marketing params, add grace period
sub vcl_recv {
# Strip tracking params from cache key
set req.url = regsuball(req.url, "[\?&](utm_[^&]+|fbclid|gclid)=[^&]*", "");
set req.url = regsub(req.url, "\?&", "?");
set req.url = regsub(req.url, "\?$", "");
}
sub vcl_hit {
# Serve stale for 60s while revalidating in background
if (obj.ttl >= 0s) {
return (deliver);
}
if (obj.ttl + 60s > 0s) {
return (deliver);
}
}
# Cache-Control from origin: public, max-age=300, stale-while-revalidate=60, stale-if-error=86400
Advanced Patterns and Troubleshooting the Issues Your Dashboard Won't Flag
Green status pages lie by omission. Your provider will show 99.99% edge availability while your real user monitoring is screaming about 2.4s LCP in Southeast Asia. That disconnect usually comes from three places: mid-mile congestion, TLS negotiation overhead, and origin connection coalescing gone wrong. We've debugged cases where TTFB was 45ms in Virginia but 380-450ms in Jakarta — not because the edge was far, but because the origin was still in us-east-1 and every MISS had to cross the Pacific. Adding a shield in Singapore cut that MISS TTFB to 110ms, even though the "official" edge latency barely changed.
Another killer is dynamic content acceleration that's misconfigured. If you're using the CDN for API acceleration but haven't tuned connection pooling and TCP settings, you're adding a hop instead of removing one. Check your origin-facing keepalive settings. If your edge is opening a new TLS connection per request, you're paying a 60-90ms penalty on every single API call. You want persistent connections with a 60-90 second idle timeout and proper HTTP/2 or HTTP/3 to origin where supported. And for personalization — don't bypass cache entirely with Cache-Control: private. Use edge-side includes (ESI) or Cloudflare Workers / Fastly Compute@Edge to assemble the page: cache the 95% that's static for 5 minutes, and fetch the 5% that's personalized via a sub-request. It'll take your hit ratio from 0% on those pages to 85%+.
When you're ready to prove these patterns work under load, don't just flip a switch. A disciplined CDN Pilot Testing Methodology: Metrics, Duration, and Success Criteria is critical — we recommend a minimum 7-day pilot that covers a weekday peak and a weekend trough, measuring TTFB, hit ratio, origin 5xx, and cache revalidation rate side-by-side. And once the numbers are solid, you'll need a safe way to ramp. A phased CDN Traffic Cutover Strategy: Migrating Without Downtime or Data Loss using weighted DNS or Anycast steering lets you go from 10% → 50% → 100% with automated rollback if error rate ticks up by even 0.1%.
Frequently Asked Questions
What's a good cache hit ratio target after optimization?
It depends on content type, but here's what we see in healthy setups: 95-98% for static assets (images, JS, CSS), 85-92% for semi-dynamic pages (product listings, blogs with 5-15 min TTL + SWR), and 0% isn't always wrong for truly personalized APIs. If you're below 88% blended for a mostly-static site, you've got query string or Cookie normalization work to do. Don't chase 99%+ blindly — you'll end up serving stale personalization.
Our dashboard shows low edge latency, but real users report slowness. What should we check?
Your CDN dashboard measures edge-to-user latency, not origin-to-edge. Pull RUM data and segment by cache status: HIT vs. MISS vs. PASS. If HITs are 35ms and MISSes are 400ms+, your bottleneck is origin or mid-mile. Check shield configuration, origin location vs. user geography, and whether you're inadvertently passing cookies or Authorization headers that force every request to PASS through to origin.
How long should we run a performance pilot before making a decision?
At least 7 full days, ideally 14. Anything shorter and you'll miss cache warm-up effects and weekly traffic patterns. We've seen hit ratios start at 68% on day one and climb to 91% by day four as the cache fills. Define your success criteria upfront — for example: p95 TTFB under 120ms, hit ratio above 90%, and origin offload above 85% — and don't call it early because day two looked good.
Can we optimize without migrating to a new provider?
Absolutely. In about 70% of our reviews, the biggest gains — 30-50% lower origin load and 40-80ms p95 improvement — come from tuning the current provider: fixing cache keys, enabling shielding, adding stale-while-revalidate, and collapsing query strings. Only migrate if you've hit architectural limits like poor PoP density in your core markets or lack of edge compute for your use case.