你的网站在本机加载只需要 300 毫秒。部署一个内容分发网络(CDN)后,你把它叫做 done。另一个大陆的用户却报告加载缓慢,监控面板上也没有异常。原因通常是:一台机器的测试结果只说明这一个位置的表现,并不能代表全球其他位置的表现。
读完本文后,你应该能回答这些问题:
- DNS 解析你的域名需要多久?
- 建立连接需要多久?
- TLS 握手需要多久?
- 第一个字节到达需要多久?
- CDN 是否真的从缓存响应了请求?
- 不同区域的性能是否不同?
为什么命令行适合这项工作
浏览器类工具给出的是整体加载时间。它把网络延迟、JavaScript 执行、渲染时间混在一起,很难判断时间到底花在哪一步。
curl 会分别测量请求的每个阶段,并且完全跳过浏览器渲染,因此数值只反映网络和服务器的响应。
下面这条命令测量单个请求:
curl -sS -o /dev/null -w \
'DNS: %{time_namelookup}s
Connect: %{time_connect}s
TLS: %{time_appconnect}s
TTFB: %{time_starttransfer}s
Total: %{time_total}s
IP: %{remote_ip}
' \
https://example.com/
这使用 curl 测量到 example.com 的 HTTPS 连接计时,不下载实际网页内容。
预期输出:
DNS: 0.049033s
Connect: 0.078883s
TLS: 0.114448s
TTFB: 0.150471s
Total: 0.150763s
IP: 104.20.23.154
这条命令丢弃响应体(-o /dev/null),只打印计时数据。
输出中各项指标:
- DNS(
time_namelookup):把域名解析成 IP 的时间。 - 连接(
time_connect):建立TCP connection连接的时间。 - TLS(
time_appconnect):TLS/SSL 握手时间。 - TTFB(
time_starttransfer):首字节时间,即服务器响应开始到达的时间。 - 总计(
time_total):完整请求/响应时间。 - IP(
remote_ip):连接实际使用的 IP 地址。
这些数据可用于:
- 性能监控:检查服务器响应速度。
- 调试:分辨慢在 DNS、网络、TLS 还是服务器处理。
- 健康检查:验证服务器可用且响应正常,不下载大体积内容。
把一个请求变成性能剖面
单独的“1.2 秒”说明不了多少问题。一次请求实际上分阶段发生,每个阶段都可以单独测量。
阶段顺序是:
DNS lookup
↓
TCP connection
↓
TLS handshake
↓
Request sent
↓
First byte received
↓
Transfer completed
这些 curl 计时变量分别对应上述阶段:
time_namelookup:把域名解析成 IP 地址的时间。time_connect:向该 IP 打开TCP connection连接的时间。time_appconnect:完成TLS handshake(建立加密连接的过程)的时间。time_starttransfer:响应首字节到达前的时间,即 TTFB。time_total:整个请求完成的时间。
这样你可以定位慢的真正原因,而不是靠猜:
- DNS 时间高,问题通常在 DNS。
- 连接时间高,问题通常在网络或路由。
- TTFB 高但连接时间正常,问题通常在服务器或 CDN 响应。
- TTFB 与总时间差距大,问题通常在大响应体或慢传输。
CDN 真的在响应请求吗
响应快并不一定代表 CDN 在工作。它可能只是在把每个请求转发给你的源站,而不是从缓存服务。用响应头可以验证这一点。
下面的命令只查看响应头,不下载响应体:
curl -s -o /dev/null -D - https://example.com/ | grep -iE '(cache|age|served-by)'
示例输出:
age: 6687
cf-cache-status: HIT
这会发送普通 GET 并丢弃正文。不要用 curl -I 做这个检查:它会发送 HEAD 请求,而真实用户几乎总是发送 GET,某些 CDN 对 HEAD 的处理方式和 GET 不同,测试结果可能和用户实际看到的缓存状态不一致。
具体看哪个响应头,取决于 CDN:
- Cloudflare 使用
CF-Cache-Status,值包括HIT、MISS、EXPIRED、REVALIDATED、BYPASS、DYNAMIC。 - Fastly 使用
X-Cache,值为HIT或MISS;另有X-Cache-Hits表示该对象已从缓存服务的次数。Fastly 还会设置X-Served-By标明具体缓存节点,但这个头本身不代表命中或未命中。 - Amazon CloudFront 使用
X-Cache,格式为Hit from cloudfront或Miss from cloudfront。 - Akamai 和旧版 Azure CDN 也会使用
X-Cache,值类似TCP_HIT、TCP_MISS,但暴露方式不同。Azure CDN(经典)通常在普通响应中返回X-Cache。Akamai 的X-Cache主要是诊断头,通常需要显式请求;过去通过 Pragma 头,如Pragma: akamai-x-cache-on,可选地结合akamai-x-get-cache-key。因此,普通 Akamai 响应中没有X-Cache不能当作缓存未命中。Akamai 现在建议使用需要身份验证的 Enhanced Debug 机制。
如果响应里有 Age 头,它表示内容已经在缓存中存放的秒数。
响应很快但状态是 MISS,说明 CDN 只是放行了请求。响应很快且状态是 HIT,才说明 CDN 确实在命中缓存。
这样测试时要注意:
curl 默认不会发送浏览器会发送的头,比如 Accept-Encoding。
包括 CloudFront 在内的部分 CDN 会在启用压缩时把规范化的 Accept-Encoding 放进缓存键。没有这个头,curl 会被当作请求未压缩内容,并单独缓存一份,和浏览器拿到的压缩版本分开。于是第一次 curl 可能显示未命中,而浏览器却一直命中。
如果你的 curl 测试显示未命中,但真实用户加载很快,先检查这种不匹配,再判断是否存在真正的缓存问题。
关于 Azure 的说明: 原文章称 Microsoft 正以 Azure Front Door 取代 Azure CDN Standard from Microsoft(classic)。旧版停止新配置创建和域名接入后,部署仍可继续运行;新部署应改用 Azure Front Door Standard 或 Premium。Front Door 也暴露
X-Cache,值包括TCP_HIT、TCP_REMOTE_HIT、TCP_MISS,但诊断头体系更新、更完整。
一次测量不够
单个请求可能有误导性。第一次请求可能命中空缓存,后面的请求可能命中热缓存;网络状况也会逐秒变化。
循环运行同一请求多次:
for i in {1..10}; do
curl -sS -o /dev/null -w \
'%{http_code} %{time_starttransfer}s %{time_total}s\n' \
https://example.com/
done
示例输出:
200 0.146150s 0.146868s
200 0.231786s 0.232220s
200 0.077217s 0.077753s
200 0.240986s 0.241477s
200 0.682164s 0.682240s
200 0.132748s 0.133461s
200 0.410818s 0.933873s
200 0.087598s 0.088383s
200 0.256964s 0.745285s
200 0.679765s 0.882171s
看十次结果的整体模式,而不是只看一行。冷缓存往往表现为第一次较慢,随后变快。
数值稳定说明缓存稳定;波动很大则值得继续追查。中位数通常比平均值更有用,因为一个慢速异常值会把平均值拉偏。
CDN 缓存到底在做什么
CDN 的核心工作是缓存,不只是让服务器离用户更近。先理解几个术语:
- 缓存命中:CDN 找到有效的本地副本并直接返回。
- 缓存未命中:CDN 没有找到有效副本,必须回源。
- TTL(生存时间):CDN 在把缓存副本标记为过期前会保留多久。
- 缓存键:CDN 用来判断两个请求能否共享同一份缓存内容的一组值。
- 回源请求:只有缓存未命中时,CDN 才向你的源站发送的请求。
- 边缘节点:靠近用户的 CDN 服务器,缓存内容在这里保存和返回。
缓存键比很多人以为的更重要。如果缓存键里混入了多余的请求头、Cookie 或查询参数,会产生更多缓存变体。变体越多,命中率越低。保持缓存键简单通常能提高命中率。
Linux 特有的岔路:IPv4 与 IPv6
大多数现代系统同时支持两种协议。具体性能取决于你的网络路径和 CDN 的路由策略。
分别测试:
curl -4 -sS -o /dev/null -w '%{time_total}\n' https://example.com/
curl -6 -sS -o /dev/null -w '%{time_total}\n' https://example.com/
如果其中一个明显更慢,可能是指向某种协议的路由问题。
检查 HTTP/3 性能
主流 CDN 大多已支持 HTTP/3。它运行在 QUIC 之上,QUIC 又基于 UDP。QUIC 消除了 head-of-line blocking(队头阻塞):单个丢包不再拖慢整条连接上的所有流。因此,在有丢包的网络(例如移动网络)上,HTTP/3 可能明显更快。
你的 curl 需要带 QUIC 支持才能测这一项,多数 Linux 发行版默认安装中并未包含。检查构建是否支持:
curl --version | grep -i http3
如果没有 HTTP/3 支持,仍可通过 Alt-Svc 响应头确认服务器是否提供该协议:
curl -s -o /dev/null -D - https://example.com/ | grep -i alt-svc
如果构建支持,直接和 HTTP/2 对比:
FORMAT='Protocol: %{http_version} Total: %{time_total}s\n'
curl -s -o /dev/null --http2 -w "$FORMAT" https://example.com/
curl -s -o /dev/null --http3 -w "$FORMAT" https://example.com/
检查输出中的 %{http_version} 值确认实际使用的协议。
请求 --http3 时,curl 会先尝试 QUIC,QUIC 失败或太慢时自动回退到 HTTP/2。回退是静默发生的,即使 HTTP/3 不可用,请求也会成功。版本字段是判断真实协议的可靠方法。
DNS 是路径的一部分,不只是查询
DNS 解析不是独立于 CDN 性能的一步。CDN 经常用 DNS 把用户引导到最近的边缘节点,因此不同位置拿到的 IP 可能不同。
用 dig 查看当前解析:
dig example.com
只查看解析出的 IP,可以用更短的形式:
dig +short example.com
要区分三件事:DNS 解析这个过程、解析返回的 IP、最终处理 HTTP 请求的物理 CDN 节点。dig 只回答前两件事,不能确认是哪个物理设施响应了请求;响应头是更好的信息来源。
一台机器,一个观测点
到这里为止,所有测试都来自同一台机器、同一个位置。测量本身是准确的,但它只描述了你所在位置的性能。
CDN 根据请求来源把用户分到不同边缘节点。对你表现良好的 CDN,可能把另一个区域的用户送到了慢或配置错误的节点,而你在自己的机器上根本看不到。
下一步是在多个区域运行同样的测试并比较结果。你要看的不是某一个最快值,而是模式:
- 是否某个区域持续偏慢?
- TTFB 是否随区域大幅变化?
- 相同内容在不同区域的缓存状态是否不同?
- 某些区域是否路由到了意外节点?
- IPv4 与 IPv6 的表现是否随区域变化?
从多个区域发出请求
要测试区域差异,请求必须真的从目标区域发出。常用做法有三种。
第一种:使用你已经部署在目标区域的服务器或分支机构。控制力最强,但前提是那些区域本来就有基础设施。
第二种:在目标区域临时创建云主机,例如 DigitalOcean 或 Hetzner。适合偶尔测试,但每次都要创建并销毁实例。
第三种:通过地理分布的代理端点发送测试流量。对不想在每个区域维护测试服务器的团队,某些测试方案会使用专用轮换代理提供可重复的区域端点池。
不同方式在控制力、准备成本和测试频率之间取舍。按你的实际需求选择。
建立可重复的测试
能跨区域发请求后,把前面的内容合并成一个脚本。每个请求记录:
- 时间戳
- 区域
- HTTP 状态码
- 远端 IP
- DNS 时间
- 连接时间
- TLS 时间
- TTFB
- 总时间
输出格式类似:
timestamp,region,status,remote_ip,dns,connect,tls,ttfb,total
2026-09-05T09:00:00Z,eu-west,200,104.16.1.1,0.012,0.034,0.089,0.145,0.210
2026-09-05T09:00:00Z,ap-southeast,200,104.16.2.5,0.028,0.061,0.140,0.310,0.402
可以把输出导入 CSV、SQLite 之类的轻量数据库,或 Prometheus 之类的监控系统。用 cron 定时运行,就把一次性检查变成了持续的性能记录。
解读结果
拿到跨区域、跨请求的数据后,用这些模式判断下一步:
- DNS 时间高:检查 DNS 配置和解析器性能。
- 连接时间高、DNS 时间正常:检查到该区域的网络距离和路由。
- TTFB 高、连接时间正常:检查 CDN 或源站处理,以及缓存状态。
- TTFB 低、总时间高:检查响应体积和传输速度。
- 某个区域持续偏慢:检查该区域的路由、DNS 调度和 CDN 覆盖。
- 重复请求之间波动很大:依据多次请求的模式判断,不要依赖单次结果。
curl 不会告诉你什么
命令行测试的边界需要讲清楚:curl 测量的是网络和 HTTP 层性能,不运行 JavaScript、不渲染页面、不按浏览器顺序加载图片,也不测量布局偏移。
因此 CLI 测试不能替代浏览器端性能测试,只能作为补充。用 curl 隔离网络和服务器问题;用浏览器开发者工具或合成监控服务测量完整用户体验,包括渲染和 client-side 行为。
结论:测量用户实际体验到的内容
CDN 不是“开了就算成功”。真正的问题是它能否把内容快速、稳定地送到用户实际所在的位置。
工作流程很简单:测量、检查、重复、比较、诊断,然后再测量。先从本机单次请求开始,确认 CDN 确实在缓存,再重复排除噪声。
然后把同样的方法扩展到多个区域,使用已有基础设施、临时云主机或分布式代理端点。命令行工具可以完成这套流程,不必依赖付费监控服务。关键是养成检查的习惯。



