返回博客列表

CDN缓存命中怎么看?HIT、MISS和Age怎么理解

CdnChart 技术团队发布于 2026-09-1415 分钟阅读
CDN缓存命中怎么看?HIT、MISS和Age怎么理解

CDN已经接入,静态文件也能正常访问,但打开响应头一看:

CF-Cache-Status: MISS
Age: 0

刷新一次,又变成:

CF-Cache-Status: HIT
Age: 18

很多人看到这里会开始犯嘀咕:第一次 MISS 是不是说明CDN没有生效?Age: 18 是缓存还剩18秒,还是这个文件已经存在18秒?如果一直没有 Age,是不是所有请求都在回源?

其实,判断CDN缓存不能只截取一个字段,也不能只请求一次。你需要把缓存状态、Age、缓存控制头、请求URL和检测节点放在一起看。

先给出最短的答案:

字段或状态

通常表示什么

不能直接得出什么结论

HIT

当前缓存层找到了可复用的响应,并直接提供给本次请求

不代表所有地区、所有URL都命中

MISS

当前缓存层没有找到可直接使用的对象,需要向上游取内容

不一定表示每一层都没有缓存,也不等于CDN没生效

Age: 18

响应自源站生成或最近一次成功验证后,估算已经过去18秒

不是响应耗时,也不一定是剩余TTL

下面把这三个概念拆开讲清楚。

先确认:你看到的是哪家CDN的缓存头

HITMISS 是常见叫法,但CDN缓存状态并没有一个所有厂商完全一致的响应头名称。

你可能看到:

CF-Cache-Status: HIT

也可能看到:

X-Cache: Hit from cloudfront

还可能是某家厂商自己的 X-Cache-StatusX-CDN-Cache,或者只在控制台和访问日志里提供结果。相同单词在不同厂商、多层缓存和反向代理中的具体含义,也可能略有差异。

所以第一步不是死记字段,而是先确定:

  1. 当前域名使用了哪家CDN;

  2. 哪个响应头是这家CDN提供的;

  3. 网站前面是否还有另一层CDN、负载均衡或反向代理。

如果厂商不确定,可以先用 CdnChart CDN检测 查看CNAME、响应IP、ASN和HTTP响应头等线索,再对照厂商官方文档解释缓存状态。关于识别逻辑,也可以阅读如何判断网站使用了哪家CDN

HIT是什么意思?

HIT 的核心含义是:当前负责响应的缓存层里,已经有一个满足本次请求条件、仍然可以使用的缓存对象。

请求过程大致是:

用户请求 → CDN节点查找缓存 → 找到可用对象 → 直接返回

与回源获取完整文件相比,缓存命中通常可以减少源站请求和跨区域传输。

不过,看到一次 HIT,只能证明这一次请求、这个URL、这个节点的缓存命中了。

它不能证明:

  • 其他城市或运营商的节点也已经缓存;

  • 首页引用的所有图片、脚本和接口都命中;

  • 不同查询参数、Cookie或请求头使用同一份缓存;

  • 整个网站的缓存命中率很高;

  • 当前配置一定安全、合理。

例如下面两个URL,在很多配置中属于两个不同的缓存键:

https://example.com/app.js?v=100
https://example.com/app.js?v=101

即使第一个URL已经 HIT,第二个URL第一次访问仍可能 MISS。如果系统给每次请求都追加随机参数,那么缓存就很难被复用。

MISS是什么意思?是不是一定回源了?

MISS 通常表示:当前缓存层没有找到可以直接用于本次请求的对象。

最常见的过程是:

用户请求 → 边缘节点未命中 → 从源站获取 → 返回并按规则缓存

所以,新文件第一次被某个节点访问时出现 MISS 很正常。只要响应允许缓存,第二次请求同一个URL,就可能变成 HIT

但在现代CDN架构里,不能把所有 MISS 都简单翻译成“这次请求一定直接打到源站”。

原因是CDN可能存在两级甚至多级缓存:

用户 → 边缘节点 → 上层缓存 / Origin Shield → 源站

边缘节点本地没有对象,可以记一次本地 MISS;但上层缓存可能已经有内容,于是请求由上层缓存满足,源站并没有再次传输完整文件。

不同厂商暴露的是本地层、最终层还是汇总状态,要以其文档和日志为准。

此外,当多个用户几乎同时请求一个尚未缓存的大文件时,一些CDN会合并这些回源请求:第一个请求负责填充缓存,其余请求等待并复用结果。AWS在CloudFront自定义源站请求行为中将这种机制称为request collapsing,可以避免每个并发请求都单独访问源站。

因此,判断请求是否真正到达源站,最可靠的证据通常来自:

  • CDN控制台的回源请求数和回源流量;

  • CDN访问日志中的详细缓存状态;

  • 源站访问日志;

  • Origin Shield或上层缓存指标。

单个客户端看到的一个 MISS,只是线索,不是整个缓存链路的完整账单。

Age是什么意思?

Age 是标准HTTP响应头,单位是秒。

根据 RFC 9111 HTTP Caching,它表示缓存对“自响应由源站生成或最近一次成功验证以来已经过去多少秒”的估算。这个数值还会计入响应在缓存链路中的停留时间和传输时间。

例如:

Cache-Control: public, max-age=3600
Age: 600

可以粗略理解为:这份响应自生成或验证后已经过去约600秒。

如果没有其他缓存策略覆盖,并且 max-age=3600 确实是共享缓存采用的TTL,那么理论上还剩约3000秒的新鲜时间:

3600 - 600 = 3000秒

但这只能作为近似判断。

CDN可能配置了独立的边缘TTL,可能优先读取 s-maxageCDN-Cache-Control,也可能存在分层缓存、重新验证和厂商自定义规则。所以,不能只凭 Age 反推最终过期时间。

Age不是什么

Age 很容易被误读。它不是:

  • 本次请求花费的时间;

  • 文件从创建至今的年龄;

  • DNS缓存的TTL;

  • 缓存还剩多少秒;

  • CDN节点运行了多久;

  • 静态资源的版本号。

Age: 120 不代表页面加载用了120秒,也不代表缓存只剩120秒。

Age为什么会越来越大?

在同一个缓存节点连续访问同一个URL时,如果每次都复用同一个缓存对象,Age 往往会随时间增大:

# 第一次
CF-Cache-Status: HIT
Age: 21

# 几秒后
CF-Cache-Status: HIT
Age: 27

这是比较典型的正常命中表现。

Age为什么会突然变小或归零?

常见原因有:

  • 请求被调度到另一个边缘节点;

  • 缓存被主动清除;

  • 对象过期后重新从源站获取;

  • 缓存对象被淘汰,再次填充;

  • CDN向源站重新验证后重置了年龄;

  • 多层缓存返回了不同层的年龄信息。

Cloudflare在缓存响应状态说明中指出,其 Age 会在重新验证、清除或驱逐后重置;使用分层缓存时,还可能继承上层缓存的 Age

没有Age是不是就没有命中?

不是。

RFC 9111同时提醒:没有 Age,不能据此断定请求一定联系了源站。某些缓存或代理可能不公开该字段,某些响应状态也可能不带它。

反过来,有 Age 也不该脱离其他字段单独判断。有的上游代理或源站会传来自己的 Age,CDN也可能把它继续传递。

最好同时查看缓存状态、Via、厂商特征头和缓存控制策略。

除了HIT和MISS,还会看到哪些状态?

不同厂商的名称并不完全相同。下面是常见语义和典型示例,解释具体网站时,应以对应厂商的官方定义为准。

状态

常见含义

排查重点

HIT

找到可用缓存并返回

再看Age是否合理、不同地区是否一致

MISS

当前缓存层没有可直接使用的对象

首次访问、缓存键、TTL、是否成功写入缓存

BYPASS

对象原本可能进入缓存流程,但因响应头或规则绕过缓存

privateno-storeSet-Cookie、授权请求

DYNAMIC

请求在查缓存前就被判定为不适合缓存

URL规则、文件类型、缓存模式、动态页面

EXPIRED

已有对象过期,需要从上游取得新响应

TTL是否过短、源站是否正常

REVALIDATED/RefreshHit

上游确认内容未变化后继续使用缓存

ETagLast-Modified、条件请求

STALE

缓存已过期,但在特定条件下仍返回旧副本

源站故障、stale策略、一致性要求

UPDATING

先返回旧缓存,同时在后台更新

是否开启后台刷新

NONE/UNKNOWN

响应在常规缓存流程之前产生,或未得到可识别状态

WAF、边缘函数、重定向和错误页

这里有两个容易混淆的点。

第一,BYPASSDYNAMIC 都可能表现为请求去了上游,但原因不同:前者通常是响应头或规则让对象不被缓存,后者可能在请求阶段就被认定为动态内容。

第二,REVALIDATED 不等于重新下载整个文件。

CDN可以通过 If-None-MatchIf-Modified-Since 向源站确认内容是否变化。如果源站返回 304 Not Modified,缓存便可以继续使用原对象。

CloudFront的日志中也使用 RefreshHit 表示对象过期后向源站确认,并继续使用缓存对象,具体定义可查阅AWS CloudFront标准日志字段

怎样实际检查CDN缓存是否命中?

最实用的方法不是在浏览器里反复按刷新,而是固定条件,连续请求同一个公开静态资源。

第一步:选择适合测试的URL

优先选择:

  • 公共CSS或JavaScript文件;

  • 网站Logo、图片或字体;

  • 明确允许CDN缓存的下载文件;

  • 不需要登录、Cookie或授权头的资源。

不要一开始就拿首页、登录页、购物车或API接口测试。它们可能本来就不应该被共享缓存。

为了追求 HIT 而缓存私人页面,反而可能造成用户数据泄露。

第二步:查看完整响应头

把示例URL替换成你的真实静态资源:

curl -sS -D - -o /dev/null https://www.example.com/assets/app.css

参数含义:

  • -D -:把响应头输出到终端;

  • -o /dev/null:不保存响应正文;

  • -sS:减少进度信息,但保留错误提示。

有人习惯使用 curl -I 发送HEAD请求。它适合快速查看响应头,但有些CDN、源站或缓存规则对HEAD和GET的处理不同。

排查真实浏览器访问时,用GET请求并丢弃正文通常更接近实际情况。

重点观察这些字段:

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: ...

第三步:连续请求同一个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
done

测试时要保持这些条件一致:

  • 完整URL相同,包括大小写和查询参数;

  • 不要每次附加随机时间戳;

  • 请求方法相同;

  • 不要一会儿带Cookie,一会儿不带;

  • 不要主动发送 Cache-Control: no-cache,除非正在测试重新验证;

  • 尽量在短时间内从同一网络连续请求。

Google Cloud CDN的故障排查文档同样建议使用 curl -s -D - -o /dev/null URL 查看响应头,并以 Age 等字段辅助确认缓存响应。

连续测试结果应该怎么判断?

下面几种模式比单次结果更有参考价值。

连续结果

常见解释

下一步

第一次 MISS,随后 HIT,Age增加

节点首次取回后成功缓存

再测试其他地区和关键资源

连续多次都是 MISS

对象未写入、缓存键变化、TTL过短或频繁淘汰

检查缓存头、规则、参数和日志

一直是 BYPASSDYNAMIC

内容按规则未进入共享缓存

确认是业务设计还是配置错误

HIT,但Age偶尔归零

可能切换节点、清缓存、重新验证或重新填充

同时记录响应IP、节点和时间

一个地区 HIT,另一个地区 MISS

不同节点拥有独立缓存

各地区连续测试

外层显示 HIT,另一厂商字段显示 MISS

可能存在多层CDN或残留响应头

结合IP、CNAME和日志判断

浏览器显示 304

浏览器或中间缓存进行了条件验证

不能自动等同于CDN HIT

最理想的结果一定是“全部HIT”吗?

不是。

公开的版本化静态资源适合长期缓存,高命中通常是好事;但登录接口、个人资料、订单、购物车和实时权限数据,本来就不应该被不同用户共享缓存。

缓存优化的目标不是让所有URL变成 HIT,而是让应该缓存的内容稳定命中,不应该共享的内容始终保持隔离

为什么CDN一直MISS?

如果同一个静态资源连续测试仍然 MISS,可以按下面顺序排查。

1. 响应本身不允许共享缓存

检查源站和CDN返回的:

Cache-Control: private
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: max-age=0
Set-Cookie: ...

这些字段的影响并不完全相同:

  • no-store 表示不应存储响应;

  • private 表示共享缓存不应存储,只可由私有缓存处理;

  • no-cache 并非“绝对不能存”,而是复用前需要验证;

  • max-age=0 会让响应立即变得不新鲜;

  • Set-Cookie 在部分CDN默认策略中会导致绕过缓存。

不要盲目删除这些头。先确认它们是否在保护用户状态和敏感内容。

2. 每次请求使用了不同的缓存键

常见原因包括:

  • URL查询参数变化;

  • Host 不同;

  • Cookie被纳入缓存键;

  • Authorization 请求被单独处理;

  • Accept-Encoding、语言或设备头参与缓存变化;

  • Vary 导致缓存被拆成多个版本。

Google Cloud CDN的缓存工作原理指出,URL、查询参数和被纳入缓存键的请求头都会影响对象能否复用。

一个页面看似请求同一文件,实际缓存键可能一直在变化。

3. 缓存规则没有覆盖这个URL

检查路径、扩展名、响应状态码和请求方法是否符合CDN规则。

常见情况是只缓存 .jpg.css.js,而实际文件使用了没有扩展名的动态路径;或者规则优先级被另一条“绕过缓存”配置覆盖。

4. TTL过短或对象被频繁淘汰

如果TTL只有几秒,下一次请求到达时对象可能已经过期。

冷门对象也可能因为节点容量策略被提前淘汰,因此缓存时间上限不等于保证驻留时间。

5. 每次访问的不是同一个节点

Anycast、DNS调度、多CDN和IPv4/IPv6差异,都可能让连续请求进入不同节点。

测试时可以同时记录远端IP:

curl -sS -D - -o /dev/null \
  -w 'remote_ip=%{remote_ip} total=%{time_total}\n' \
  https://www.example.com/assets/app.css

如果响应IP不断变化,Age和缓存状态重置就更容易解释。

6. 测试的是HTML、接口或带身份状态的请求

许多CDN默认缓存静态文件,却不默认缓存HTML和动态接口。此时持续 DYNAMICBYPASS 可能正是预期结果。

先拿已知可缓存的公共静态资源建立基准,再排查动态内容。

为什么不同地区看到的HIT、MISS和Age不一样?

CDN的缓存不是全球只有一份。

北京、新加坡、法兰克福和洛杉矶的用户可能进入不同的边缘节点,每个节点的访问热度、填充时间和淘汰状态都不同。

因此,同一个URL可能同时出现:

地区

缓存状态

Age

可能情况

北京

HIT

850

当地访问频繁,缓存较热

新加坡

HIT

42

最近刚填充或重新验证

法兰克福

MISS

该节点首次访问或对象已被淘汰

这并不矛盾,也不代表某个检测点出错。

可以先用 CdnChart网站测速 对同一URL做多地区测试,查看各地区的响应IP、耗时和访问差异,再结合自己用 curl、浏览器开发者工具或CDN日志取得的缓存响应头判断。

CdnChart的探测口径可以在测评方法说明中查看。

需要注意:一次跨地区测试仍然只是各节点当时的快照。要判断缓存是否稳定,最好固定URL,在相同地区连续测试,并记录时间、IP和缓存状态。

一次HIT不等于缓存命中率高

单次响应只能回答“这一次是否命中”,不能回答“网站整体缓存效果如何”。

CDN控制台里常见的命中率还可能分为:

  • 请求命中率:被缓存满足的请求数,占符合统计口径请求数的比例;

  • 字节命中率:由缓存提供的流量,占总传输字节数的比例。

例如,大量小图标命中,但几个大文件频繁回源,请求命中率可能很好看,字节命中率却不高。

反过来,一个大视频命中也可能显著抬高字节命中率。

此外,不同厂商对动态请求、错误响应、不可缓存请求和上层缓存命中的统计口径可能不同。跨厂商比较前,应先核对指标定义,不要只比较一个百分比。

真正值得长期观察的是:

  • 关键静态资源的请求命中率和字节命中率;

  • 回源请求数、回源流量与回源错误率;

  • 不同地区、路径和文件类型的命中差异;

  • 缓存清除、发布和版本更新后的变化;

  • 源站负载是否随命中提升而合理下降。

常见问题

第一次MISS、第二次HIT,说明CDN正常吗?

通常说明当前节点完成了缓存填充,是正常的冷缓存到热缓存过程。但仍需检查对象TTL、其他地区节点和长期命中率。

MISS是不是说明CDN加速没有生效?

不是。请求已经到达CDN,只是当前缓存层没有可复用对象。

CDN仍可能提供TLS终止、连接优化、安全防护或上层缓存等能力。

Age为0是HIT还是MISS?

只看 Age: 0 无法判断。它可能是一份刚写入或刚完成重新验证的缓存,也可能来自其他代理。

应同时查看厂商缓存状态头和日志。

Age越大是不是访问越慢?

不是。Age表示响应的缓存年龄,不是请求耗时。

性能要看DNS、连接、TLS、TTFB和总下载时间等指标。

Age超过max-age为什么还能收到内容?

可能发生了重新验证、允许返回stale内容、存在上层缓存策略,或者看到的 max-age 并非CDN最终采用的边缘TTL。

需要结合 s-maxageCDN-Cache-Control、厂商配置和缓存状态分析。

浏览器强制刷新后变成MISS,配置坏了吗?

不一定。

强制刷新可能携带要求重新验证或绕过本地缓存的请求头,也可能触发不同的CDN处理。排查时应使用固定的普通GET请求,避免把浏览器缓存和CDN缓存混为一谈。

如何确认请求真的访问了源站?

查看源站访问日志、CDN回源日志和回源指标最可靠。

客户端的 MISS 可以提示你继续调查,但无法完整描述多层缓存内部发生了什么。

总结:缓存命中要看一组证据,不是一个单词

记住三个核心结论:

  1. HIT 表示当前缓存层找到了可复用对象,但只代表本次请求和当前节点;

  2. MISS 表示当前缓存层未命中,不等于CDN没生效,也不一定能证明每个请求都直达源站;

  3. Age 表示响应自生成或最近一次验证后经过的估算秒数,不是响应时间,也不是天然的剩余TTL。

实际排查时,选择一个公开静态资源,保持URL和请求条件不变,连续请求三次;同时记录缓存状态、AgeCache-Control、响应IP和检测时间。

然后再换地区验证,并用CDN控制台或源站日志确认是否真正回源。

如果还不确定域名接入了哪家CDN,可以先使用 CdnChart CDN检测 查找厂商线索;如果想比较不同地区的访问节点和速度,可以使用 CdnChart网站测速

把缓存响应头与多地区测速结果放在一起,远比看到一次 MISS 就下结论更可靠。

  • CDN怎么看是否命中缓存
  • CF-Cache-Status HIT什么意思
  • CDN一直MISS怎么办
  • Age响应头是什么意思