What the ratio means
Every request that reaches a CDN edge ends one of two ways. Either the edge has a fresh copy and answers immediately — a hit — or it forwards the request to your origin and waits — a miss. The hit ratio is hits divided by all requests. A ratio near 100% on static assets is normal; a ratio near 0% means the CDN is adding a network hop and removing nothing.
Two outcomes sit in between. A revalidated response was in cache but stale, so the edge asked the origin whether it changed; the origin answered quickly, but it was still asked. A stale response was served from cache while the edge refreshed it in the background — the visitor did not wait on the origin. Responses the CDN marks as dynamic or bypass were never eligible for caching at all.
Reading the cache headers
Each CDN reports the result in its own header. The common ones:
- Cloudflare —
cf-cache-status: HIT,MISS,EXPIRED,REVALIDATED,DYNAMIC,BYPASS. Thecf-rayheader ends in the edge's airport code. - Amazon CloudFront —
x-cache: Hit from cloudfrontorMiss from cloudfront;x-amz-cf-popnames the edge. - Fastly —
x-cache: HIT, MISS, one value per cache layer (shield, then edge);x-served-bylists the nodes. - Akamai — usually exposed through
Server-Timing: cdn-cache; desc=HIT. - Vercel —
x-vercel-cache; Netlify — the standardCache-Statusheader.
Layered caches need care. When Fastly answers x-cache: HIT, MISS, the shield had a
copy and the edge did not — the origin was never asked. Counting that as a miss understates
what the CDN is doing for you.
Why a CDN misses on every request
- The response says not to cache it —
Cache-Control: private,no-storeormax-age=0, often added by a framework default. - A cookie comes back — many CDNs will not cache a response that sets one.
- The cache key is too fine — a
Varyheader onUser-Agent, or query strings that change on every request, give every visitor their own cache entry. - The TTL is shorter than the traffic gap — a quiet page expires before its next visitor arrives.
- Deploys purge everything — fine once, but a pipeline that purges on every commit keeps the cache permanently cold.
Measuring it over time
A single request tells you about one edge at one moment. The useful number is the ratio over days, measured the same way each time: request the same URL on a schedule, record hit or miss, and graph the share of hits. A drop from 95% to 10% after a deploy is a configuration regression, even though every request still returned 200.
Remember that each edge keeps its own cache. A scheduled check from one location measures the edge that location reaches; a path can be warm there and cold elsewhere. Real-user data that records the cache result per page view, where the CDN exposes it, covers the rest.
Finally, compare with the origin. Request the same path straight from the origin and compare time to first byte: that difference is what the CDN actually saves. If it is negative, the CDN is making that path slower.