How to Check CDN Cache Hits: Understanding HIT, MISS, and Age
The CDN is already enabled, and static files are loading normally, but when you open the response headers:
CF-Cache-Status: MISS
Age: 0Refresh once, and it changes to:
CF-Cache-Status: HIT
Age: 18Many people start to wonder at this point: the first timeMISS— does that mean the CDN isn't working?Age: 18Does it mean there are 18 seconds left in cache, or that the file has existed for 18 seconds? If there is never anyAge, are all requests going back to origin?
In fact, you can't judge CDN caching from a single field or a single request. You need to look atcache status, Age, cache-control headers, request URL, and test nodetogether.
Here's the short answer first:
Field or status | What it usually means | What you can't conclude directly |
|---|---|---|
| The current cache layer found a reusable response and served it directly for this request | Does not mean all regions and all URLs are hits |
| The current cache layer did not find an object it can use directly and needs to fetch content from upstream | Does not necessarily mean no layer has a cache, nor does it mean the CDN isn't working |
| The response is estimated to be 18 seconds old since it was generated by the origin or last successfully validated | Not response time, and not necessarily the remaining TTL |
Let's break down these three concepts clearly.
First confirm: which CDN's cache header are you seeing?
HITandMISSare common names, but there is no single response header name for CDN cache status that all vendors use identically.
You might see:
CF-Cache-Status: HITYou might also see:
X-Cache: Hit from cloudfrontIt could also be a vendor-specificX-Cache-Status,X-CDN-Cache, or the result may only be available in the console and access logs. The same word can also have slightly different meanings across vendors, multi-layer caches, and reverse proxies.
So the first step is not memorizing fields, but determining:
Which CDN the current domain uses;
Which response header is provided by that CDN;
Whether there is another CDN, load balancer, or reverse proxy in front of the website.
If you're not sure about the vendor, you can first useCdnChart CDN Checkto look at clues such as CNAME, response IP, ASN, and HTTP response headers, then refer to the vendor's official documentation to interpret cache status. For the identification logic, you can also readHow to identify which CDN a website uses.
What does HIT mean?
HITThe core meaning is:The cache layer currently handling the response already has a cache object that meets the conditions of this request and can still be used.
The request process is roughly:
用户请求 → CDN节点查找缓存 → 找到可用对象 → 直接返回Compared with fetching the complete file from origin, a cache hit usually reduces origin requests and cross-region transfer.
However, seeing a singleHITonly proves that this one request, this URL, and this node had a cache hit.
It cannot prove:
That nodes in other cities or carriers have also cached it;
That all images, scripts, and APIs referenced by the homepage are hits;
That different query parameters, cookies, or request headers use the same cache;
That the overall website cache hit rate is high;
That the current configuration is necessarily safe and reasonable.
For example, the following two URLs are two different cache keys in many configurations:
https://example.com/app.js?v=100
https://example.com/app.js?v=101Even if the first URL is alreadyHIT, the second URL may still be aMISSon its first visit. If the system appends a random parameter to every request, the cache will be difficult to reuse.
What does MISS mean? Does it always mean a request went back to origin?
MISSusually means:The current cache layer did not find an object that can be used directly for this request.
The most common process is:
用户请求 → 边缘节点未命中 → 从源站获取 → 返回并按规则缓存So when a new file is accessed by a node for the first time, aMISSis normal. As long as the response allows caching, the second request for the same URL may become aHIT.
But in modern CDN architectures, you can't treat everyMISSas simply meaning “this request definitely went straight to origin.”
The reason is that a CDN may have two or even multiple cache tiers:
用户 → 边缘节点 → 上层缓存 / Origin Shield → 源站The edge node does not have the object locally, so it can record a localMISS; but an upper-tier cache may already have the content, so the request is satisfied by the upper-tier cache and the origin does not transfer the complete file again.
Whether a vendor exposes the local tier, final tier, or aggregate status depends on its documentation and logs.
In addition, when multiple users request a large file that is not yet cached at almost the same time, some CDNs collapse those origin requests: the first request fills the cache, and the rest wait and reuse the result. AWS, inCloudFront custom origin request behavior, calls this mechanism request collapsing, which prevents every concurrent request from individually accessing the origin.
Therefore, the most reliable evidence for whether requests truly reach the origin usually comes from:
Origin request counts and origin traffic in the CDN console;
Detailed cache status in CDN access logs;
Origin access logs;
Origin Shield or upper-tier cache metrics.
A singleMISSseen by one client is only a clue, not a complete account of the entire cache chain.
What does Age mean?
Ageis a standard HTTP response header measured in seconds.
According toRFC 9111 HTTP Caching, it represents the cache's estimate of how many seconds have passed since the response was generated by the origin or last successfully validated. This value also includes the time the response has spent in the cache chain and in transit.
For example:
Cache-Control: public, max-age=3600
Age: 600It can be roughly understood as: this response has been around for about 600 seconds since it was generated or validated.
If no other cache policy overrides it, andmax-age=3600is indeed the TTL used by the shared cache, then in theory about 3000 seconds of freshness remain:
3600 - 600 = 3000秒But this can only serve as an approximate judgment.
The CDN may have an independent edge TTL configured, may prioritize readings-maxageorCDN-Cache-Control, and may also have tiered caching, revalidation, and vendor-specific rules. Therefore, you cannot rely onAgealone to infer the final expiration time.
What Age is not
Ageis easily misread. It is not:
The time this request took;
The age of the file since it was created;
The DNS cache TTL;
How many seconds remain in the cache;
How long the CDN node has been running;
The version number of a static asset.
Age: 120does not mean the page took 120 seconds to load, nor that only 120 seconds remain in cache.
Why does Age keep increasing?
When the same URL is repeatedly accessed on the same cache node, if the same cache object is reused each time,Ageoften increases over time:
# 第一次
CF-Cache-Status: HIT
Age: 21
# 几秒后
CF-Cache-Status: HIT
Age: 27This is a fairly typical sign of normal cache hits.
Why does Age suddenly decrease or reset to zero?
Common reasons include:
The request was routed to another edge node;
The cache was actively purged;
The object expired and was fetched from origin again;
The cache object was evicted and refilled;
The CDN revalidated with the origin and reset the age;
Multi-tier caching returned age information from different tiers.
Cloudflare, inCache Response Status documentation, states that itsAgeresets after revalidation, purging, or eviction; when tiered caching is used, it may also inherit the upper-tier cache'sAge.
If Age is missing, does that mean there was no hit?
No.
RFC 9111 also notes: if there is noAge, you cannot conclude that the request definitely contacted the origin. Some caches or proxies may not expose this field, and some response statuses may not include it.
Conversely, having anAgeshould not be judged in isolation from other fields. Some upstream proxies or origins send their ownAge, and the CDN may pass it along.
It is best to check cache status,Via, vendor-specific headers, and cache-control policies together.
Besides HIT and MISS, what other statuses might you see?
Vendor names are not exactly the same. Below are common meanings and typical examples; when interpreting a specific website, use the corresponding vendor's official definitions.
Status | Common meaning | Troubleshooting focus |
|---|---|---|
| Found a usable cache and returned it | Then check whether Age is reasonable and consistent across regions |
| The current cache layer has no directly usable object | First visit, cache key, TTL, whether it was successfully written to cache |
| The object may have entered the cache flow but bypassed caching due to response headers or rules |
|
| The request was judged not cacheable before cache lookup | URL rules, file types, cache mode, dynamic pages |
| An existing object expired and a new response is needed from upstream | Whether TTL is too short and whether the origin is healthy |
| Upstream confirmed the content has not changed, so the cache continues to be used |
|
| The cache expired, but the stale copy is still returned under specific conditions | Origin failure, stale policy, consistency requirements |
| Return the old cache first while updating in the background | Whether background refresh is enabled |
| The response was generated before the normal cache flow, or no recognizable status was obtained | WAF, edge functions, redirects, and error pages |
There are two easily confused points here.
First,BYPASSandDYNAMICcan both appear as requests going upstream, but for different reasons: the former usually means response headers or rules prevent the object from being cached, while the latter may be identified as dynamic content at the request stage.
Second,REVALIDATEDdoes not mean re-downloading the entire file.
The CDN can useIf-None-MatchorIf-Modified-Sinceto confirm with the origin whether the content has changed. If the origin returns304 Not Modified, the cache can continue to use the original object.
CloudFront logs also useRefreshHitto indicate that the object expired, was confirmed with the origin, and the cached object continues to be used. For the specific definition, seeAWS CloudFront standard log fields.
How do you actually check whether CDN cache is hit?
The most practical method is not to keep hitting refresh in the browser, but to fix the conditions and repeatedly request the same public static asset.
Step 1: Choose a suitable URL for testing
Prefer:
Public CSS or JavaScript files;
Website logo, images, or fonts;
Download files that are explicitly allowed to be cached by the CDN;
Resources that do not require login, cookies, or authorization headers.
Don't start by testing the homepage, login page, shopping cart, or API endpoints. They may be things that should not be shared-cached in the first place.
In pursuit ofHITcaching private pages may actually cause user data leakage.
Step 2: View the complete response headers
Replace the example URL with your actual static asset:
curl -sS -D - -o /dev/null https://www.example.com/assets/app.cssParameter meanings:
-D -: output response headers to the terminal;-o /dev/null: do not save the response body;-sS: reduce progress output but keep error messages.
Some people are used to usingcurl -Ito send a HEAD request. It is good for quickly viewing response headers, but some CDNs, origins, or cache rules handle HEAD and GET differently.
When troubleshooting real browser access, using a GET request and discarding the body is usually closer to actual conditions.
Focus on these fields:
Cache-Control: public, max-age=3600
CDN-Cache-Control: max-age=86400
CF-Cache-Status: HIT
X-Cache: Hit from cloudfront
Age: 245
ETag: "abc123"
Last-Modified: Thu, 10 Sep 2026 08:00:00 GMT
Via: ...Step 3: Repeatedly request the same URL
for i in 1 2 3; do
printf 'Request %s\n' "$i"
curl -sS -D - -o /dev/null https://www.example.com/assets/app.css \
| grep -Ei '^(cache-control|cdn-cache-control|age|via|x-cache|cf-cache-status|etag|last-modified):'
sleep 2
doneDuring testing, keep these conditions consistent:
The full URL is the same, including case and query parameters;
Do not append a random timestamp each time;
The request method is the same;
Do not sometimes send cookies and sometimes not;
Do not actively send
Cache-Control: no-cache, unless you are testing revalidation;Try to make repeated requests from the same network within a short period.
Google Cloud CDN'stroubleshooting documentationalso recommends usingcurl -s -D - -o /dev/null URLto view response headers, and usingAgeand other fields to help confirm cache responses.
How should you interpret repeated test results?
The following patterns are more informative than a single result.
Repeated results | Common explanation | Next step |
|---|---|---|
The first time | The node fetched it for the first time and cached it successfully | Then test other regions and key resources |
Multiple consecutive requests are | Object not written, cache key changing, TTL too short, or frequent eviction | Check cache headers, rules, parameters, and logs |
Always | Content did not enter the shared cache under the rules | Determine whether it is by design or a configuration error |
| May be switching nodes, purging cache, revalidating, or refilling | Record the response IP, node, and time at the same time |
One region | Different nodes have independent caches | Test repeatedly across regions |
The outer layer shows | There may be multi-layer CDN or residual response headers | Use IP, CNAME, and logs to determine |
The browser shows | The browser or an intermediate cache performed conditional validation | Cannot automatically be equated with CDN |
Is the ideal result necessarily “all HIT”?
No.
Public versioned static resources are suitable for long-term caching, and a high hit rate is usually good; but login APIs, personal profiles, orders, shopping carts, and real-time permission data should not be shared-cached across different users in the first place.
The goal of cache optimization is not to make every URL becomeHIT, but to makecontent that should be cached reliably hit, and content that should not be shared always remain isolated.
Why does the CDN keep showing MISS?
If the same static asset still showsMISSunder repeated testing, troubleshoot in the following order.
1. The response itself does not allow shared caching
Check what the origin and CDN return:
Cache-Control: private
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: max-age=0
Set-Cookie: ...These fields do not all have the same effect:
no-storemeans the response should not be stored;privatemeans shared caches should not store it, and only private caches may handle it;no-cachedoes not mean “absolutely cannot store,” but requires validation before reuse;max-age=0makes the response immediately stale;Set-Cookiecauses cache bypass under some CDNs' default policies.
Do not blindly remove these headers. First confirm whether they are protecting user state and sensitive content.
2. Each request uses a different cache key
Common causes include:
URL query parameters change;
Hostdiffers;Cookies are included in the cache key;
Authorizationrequests are handled separately;Accept-Encoding, language, or device headers participate in cache variation;Varycauses the cache to be split into multiple versions.
Google Cloud CDN'show caching worksstates that the URL, query parameters, and request headers included in the cache key all affect whether an object can be reused.
A page may appear to request the same file, while the actual cache key keeps changing.
3. Cache rules do not cover this URL
Check whether the path, extension, response status code, and request method match the CDN rules.
A common case is caching only.jpg,.css,.js, while the actual file uses a dynamic path with no extension; or the rule priority is overridden by another “bypass cache” configuration.
4. TTL is too short or objects are frequently evicted
If the TTL is only a few seconds, the object may already be expired when the next request arrives.
Less popular objects may also be evicted early due to node capacity policies, so a maximum cache time does not guarantee residency time.
5. Each visit does not hit the same node
Anycast, DNS scheduling, multi-CDN, and IPv4/IPv6 differences can all cause consecutive requests to land on different nodes.
During testing, record the remote IP at the same time:
curl -sS -D - -o /dev/null \
-w 'remote_ip=%{remote_ip} total=%{time_total}\n' \
https://www.example.com/assets/app.cssIf the response IP keeps changing, resets of Age and cache status are easier to explain.
6. You are testing HTML, APIs, or requests with identity state
Many CDNs cache static files by default but do not cache HTML and dynamic APIs by default. In this case, continuedDYNAMICorBYPASSmay be exactly the expected result.
First establish a baseline with a known cacheable public static resource, then troubleshoot dynamic content.
Why do HIT, MISS, and Age differ across regions?
CDN cache is not a single global copy.
Users in Beijing, Singapore, Frankfurt, and Los Angeles may reach different edge nodes, and each node differs in access popularity, fill time, and eviction status.
As a result, the same URL may simultaneously show:
Region | Cache status | Age | Possible situation |
|---|---|---|---|
Beijing |
| 850 | Frequently accessed locally, cache is relatively hot |
Singapore |
| 42 | Recently filled or revalidated |
Frankfurt |
| None | First access on this node, or the object has been evicted |
This is not contradictory, nor does it mean a test point is wrong.
You can first useCdnChart Website Speed Testto run multi-region tests on the same URL, check response IP, latency, and access differences by region, and then combine that with your owncurl, browser developer tools, or CDN logs to judge the cache response headers.
CdnChart's probing methodology can be found inTesting Methodology.
Note: a single cross-region test is still only a snapshot of each node at that moment. To determine whether caching is stable, it is best to fix the URL, repeatedly test in the same region, and record the time, IP, and cache status.
One HIT does not mean a high cache hit rate
A single response can only answer “did this one hit,” not “how well is the website caching overall.”
Hit rates commonly seen in CDN consoles may also be divided into:
Request hit rate: the proportion of requests satisfied by cache among requests that meet the statistical criteria;
Byte hit rate: the proportion of traffic served by cache among total transferred bytes.
For example, if many small icons are hits but a few large files frequently go back to origin, the request hit rate may look good while the byte hit rate is not high.
Conversely, one large video hit can significantly raise the byte hit rate.
In addition, different vendors may use different statistical criteria for dynamic requests, error responses, non-cacheable requests, and upper-tier cache hits. Before comparing across vendors, verify the metric definitions first; don't compare just one percentage.
What is truly worth observing over the long term is:
Request hit rate and byte hit rate for key static resources;
Origin request count, origin traffic, and origin error rate;
Differences in hit rates across regions, paths, and file types;
Changes after cache purges, releases, and version updates;
Whether origin load decreases reasonably as hit rates improve.
FAQ
First request MISS, second HIT — does that mean the CDN is working normally?
It usually means the current node completed cache fill, a normal cold-to-warm cache process. But you should still check the object TTL, nodes in other regions, and the long-term hit rate.
Does MISS mean CDN acceleration is not working?
No. The request has reached the CDN; it's just that the current cache layer has no reusable object.
The CDN may still provide TLS termination, connection optimization, security protection, or upper-tier caching.
Is Age 0 a HIT or a MISS?
Looking only atAge: 0is not enough to tell. It could be a cache just written or just revalidated, or it could come from another proxy.
You should check the vendor's cache status header and logs together.
Does a larger Age mean slower access?
No. Age indicates the cache age of the response, not request latency.
Performance should be measured by metrics such as DNS, connection, TLS, TTFB, and total download time.
Why can content still be received when Age exceeds max-age?
Revalidation may have occurred, stale content may be allowed, an upper-tier cache policy may exist, or themax-ageyou see is not the edge TTL ultimately used by the CDN.
You need to analyze it together withs-maxage,CDN-Cache-Control, vendor configuration, and cache status.
After a browser hard refresh it becomes MISS — is the configuration broken?
Not necessarily.
A hard refresh may carry request headers that require revalidation or bypass local cache, and may also trigger different CDN handling. When troubleshooting, use a fixed normal GET request and avoid confusing browser cache with CDN cache.
How do you confirm that a request really reached the origin?
Checking origin access logs, CDN origin logs, and origin metrics is the most reliable.
The client-sideMISScan prompt you to investigate further, but it cannot fully describe what happens inside multi-tier caching.
Summary: cache hits require a set of evidence, not a single word
Remember three core conclusions:
HITmeans the current cache layer found a reusable object, but it only represents this request and this node;MISSmeans the current cache layer missed, which does not mean the CDN isn't working, nor does it necessarily prove every request goes straight to origin;Ageindicates the estimated number of seconds since the response was generated or last validated; it is not response time, nor is it inherently the remaining TTL.
When troubleshooting in practice, choose a public static asset, keep the URL and request conditions unchanged, and make three consecutive requests; record the cache status,Age,Cache-Control, response IP, and test time.
Then verify in another region and use the CDN console or origin logs to confirm whether requests truly go back to origin.
If you're still not sure which CDN the domain uses, you can first useCdnChart CDN Checkto look for vendor clues; if you want to compare access nodes and speed across regions, you can useCdnChart Website Speed Test.
Putting cache response headers together with multi-region speed test results is far more reliable than seeing oneMISSand jumping to a conclusion.
- How to check CDN cache hit status
- What does CF-Cache-Status HIT mean
- What to do if CDN always shows MISS
- What does the Age response header mean