"我的网站打开 3 秒,TTFB 就占了 1.8 秒。"——这是站长群里几乎每天都有人在问的问题。TTFB(Time To First Byte,首字节时间)是衡量网站响应速度的核心指标,但它不是一个原子操作,而是由 6 个独立阶段串联而成。任何一段出问题都会让 TTFB 数字飙升,而排查方向截然不同。
这篇文章会把 TTFB 这一个数字拆成 6 段,每段给出健康/警戒/病态的数值基准,并告诉你每一段如果慢了应该用什么工具定位、怎么优化。读完之后再看到"TTFB 1.8 秒",你就知道该从哪一步开始查。
一、TTFB 是什么,包含哪 6 个阶段
TTFB 严格定义是:浏览器发起请求开始,到收到响应的第一个字节为止所经历的时间。看似只有"发请求—收响应"两步,实际上中间藏了 6 个子阶段:
- DNS 解析:把域名翻译成 IP 地址
- TCP 连接:浏览器和服务器握手建立 TCP 通道
- TLS 握手:HTTPS 站点协商加密通道(HTTP 站点跳过此步)
- HTTP 请求发送:把请求报文发到服务器
- 服务器处理:服务器跑业务逻辑、查数据库、渲染响应
- 首字节响应:服务器把响应第一个字节通过网络回传给浏览器
用一张表把这 6 段在浏览器开发者工具(Chrome DevTools Network 面板)里的对应字段列出来:
| 阶段 | DevTools 字段 | 健康值 | 警戒值 | 病态值 |
|---|---|---|---|---|
| DNS 解析 | DNS Lookup | < 30ms | 30-100ms | > 100ms |
| TCP 连接 | Initial connection | < 50ms | 50-150ms | > 150ms |
| TLS 握手 | SSL | < 100ms | 100-300ms | > 300ms |
| 请求发送 | Request sent | < 5ms | 5-50ms | > 50ms |
| 服务器处理 | Waiting (TTFB) | < 200ms | 200-600ms | > 600ms |
| 首字节传输 | — | < 50ms | 50-200ms | > 200ms |
注意一个常见混淆:Chrome 把 "Waiting" 字段也叫做 TTFB,但这只是"服务器处理时间",跟整体 TTFB(从发起请求到收第一字节)不是一回事。本文统一用"整体 TTFB"指完整的 6 段总和,用"服务器处理"指 Chrome 的 Waiting 字段。
二、阶段 1:DNS 解析慢——80% 的人没意识到的隐形成本
DNS 解析是请求生命周期的第一步,但很多人完全忽略它的性能影响。一个未优化的 DNS 解析在国内可能要 200-500ms,这一段比"服务器慢"还要难发现,因为它不在你的服务器日志里。
怎么判断 DNS 是不是慢
用 dig 命令直接测:
dig www.example.com @114.114.114.114
# 看输出的 Query time
dig www.example.com @8.8.8.8
# 对比不同公共 DNS
或者用本站的 DNS 查询工具,从全国多节点测试解析速度。如果不同地区差异巨大(北京 30ms、广州 280ms),说明你的权威 DNS 服务器节点不够分散。
DNS 慢的常见原因
- 使用了境外权威 DNS:很多人把域名 DNS 托管在 Cloudflare/Namecheap 等境外服务,国内解析要绕到香港/日本
- NS 记录只有一台:单点故障 + 缓存击穿时解析雪崩
- TTL 设置过短:DNS 缓存频繁失效,每次都要回源
- 权威服务器地理分布差:所有 NS 都在同一个机房
优化建议
把权威 DNS 换成国内有 BGP 节点的厂商(DNSPod、阿里云 DNS、华为云 DNS),TTL 设置 600-3600 秒(不要短于 300,太短会被频繁回源)。对核心域名可以同时配置 2-3 家 DNS 服务商做冗余。
三、阶段 2:TCP 连接慢——大多数情况只看一个数字
TCP 三次握手的时间,几乎完全等于用户到服务器的物理网络 RTT。这是 6 个阶段里最难"优化"的——光速就那么快,北京到洛杉矶单程 150ms 是物理极限。
TCP 阶段在测什么
三次握手包含 SYN → SYN-ACK → ACK。前两步耗时基本等于一个 RTT。Chrome 显示的 "Initial connection" 时间,约等于客户端到服务器的一个 RTT 加上 TCP 协议栈处理开销。
怎么判断 TCP 是不是慢
用本站的 TCPing 工具,直接对你网站的 443 端口或 80 端口测试。把测得的延迟和该地区到该机房的"基线延迟"对比:
| 路线 | 典型 RTT |
|---|---|
| 国内同省同运营商 | 5-20ms |
| 国内跨省跨运营商 | 20-60ms |
| 大陆 → 香港 | 30-80ms |
| 大陆 → 日本 | 50-120ms |
| 大陆 → 美国西海岸 | 140-200ms |
| 大陆 → 美国东海岸 | 180-280ms |
| 大陆 → 欧洲 | 200-300ms |
如果实测明显高于基线,问题不在"距离"而在"绕路"。这时要用 路由追踪 / MTR 工具 看 IP 包是从哪里开始绕的。常见的"绕路"包括:电信走美国回亚洲、联通走欧洲到日本等运营商出口问题。
优化建议
- 用 CDN 把用户和"边缘节点"的距离拉近,让 TCP 握手只在用户和最近边缘之间发生
- 启用 TCP Fast Open(TFO):让握手和首个请求合并,但需要客户端和服务端都支持
- 升级到 HTTP/3(QUIC):QUIC 基于 UDP,0-RTT 握手在重连场景下能省掉一整个 RTT
四、阶段 3:TLS 握手慢——HTTPS 的"双倍开销"
HTTPS 站点比 HTTP 站点 TTFB 高的根源就在这里。完整的 TLS 1.2 握手需要额外 2 个 RTT,TLS 1.3 优化到 1 个 RTT,会话复用可以做到 0-RTT。
TLS 握手开销分布
| TLS 版本 | 新连接 | 会话复用 |
|---|---|---|
| TLS 1.2 | 2 RTT | 1 RTT |
| TLS 1.3 | 1 RTT | 0 RTT |
所以一个用户的"首次访问"和"二次访问"TTFB 差异巨大——首次要完整握手,再访问可以走会话复用。如果你的 TLS 握手时间稳定在 300ms+ 不下来,最可能的原因不是延迟,而是握手过程中证书链验证太慢。
怎么判断 TLS 是不是慢
用 openssl 命令直接量化:
openssl s_client -connect www.example.com:443 -servername www.example.com < /dev/null
# 看输出里 "SSL handshake has read X bytes and written Y bytes" 之前的耗时
或者用本站的 HTTP 测试工具,它会单独显示 TLS 握手耗时。
TLS 慢的常见原因和优化
- 证书链太长:每多一级中间证书,浏览器要多验证一次。优化方案是让 Nginx 配置时把完整证书链(fullchain)拼起来,而不是只发服务器证书让浏览器自己去补
- OCSP 检查阻塞:浏览器要在线检查证书是否被吊销。优化方案是 Nginx 开启
ssl_stapling on - 用了较老的密钥交换算法:比如 RSA-2048。改成 ECDHE + ECDSA 可以让握手快 30-50%
- 没启用 Session Resumption:Nginx 配置
ssl_session_cache shared:SSL:10m;和ssl_session_timeout 1h; - 升级到 TLS 1.3:协议层面减少一个 RTT
五、阶段 4:请求发送——基本不会成为瓶颈
"Request sent" 是把 HTTP 报文从浏览器写到 TCP 套接字的时间,绝大多数情况下这一步在 1-5ms 之间,是 6 段里最不需要担心的。
如果这一段超过 50ms,通常是请求体异常大(比如带了几 MB 的 cookie,或者 POST 上传大文件)。检查浏览器开发者工具的 Request Headers 总大小,如果超过 16KB 要考虑减小 cookie。
六、阶段 5:服务器处理——TTFB 的"主战场"
这是 Chrome 显示的 "Waiting (TTFB)",也是大多数 TTFB 优化文章实际在讲的部分。服务器收到完整请求后到产出第一个字节的全部内部处理时间。
这一段可能慢的原因
按出现频率从高到低:
- 数据库慢查询:单个 SQL 100ms+,或者一个页面查 30 次数据库
- 缺少缓存:每次请求都从源头计算/查询
- 外部 API 阻塞调用:等支付接口、推送接口等同步返回
- PHP/Python 应用启动开销:FPM 进程不够导致排队,或 Python 应用没用 WSGI 复用
- 磁盘 IO 瓶颈:日志写入慢、模板文件多次读取
- CPU 跑满:服务器流量太大或代码效率差
怎么定位是哪个子原因
不能只看"服务器慢"四个字。在服务器端开启慢日志:
- Nginx 慢日志:
access_log加$request_time字段 - PHP-FPM 慢日志:
slowlog+request_slowlog_timeout 1s - MySQL 慢查询:
slow_query_log = ON+long_query_time = 0.5 - 应用层 APM:阿里云 ARMS、腾讯 APM、Sentry Performance 等
常用优化手段
| 问题 | 优化方案 | 预期收益 |
|---|---|---|
| 数据库慢查询 | 加索引、慢查询日志逐个查 | 50-90% |
| 无缓存 | Redis/Memcached 缓存热数据 | 80-99% |
| 页面渲染慢 | Nginx 缓存整页(fastcgi_cache) | 90-99% |
| 外部 API 阻塞 | 异步队列 + 占位响应 | 因场景 |
| PHP-FPM 进程不够 | 调大 pm.max_children | 看负载 |
| 静态资源混在动态请求里 | 分离到独立 location | 30-50% |
七、阶段 6:首字节传输——和距离相关,和带宽无关
服务器产出第一个字节后通过网络传回客户端的时间。这一段又是一个 RTT 的一半(单程),所以基本无法在应用层优化。
这一段慢通常只有一种情况:服务器到用户之间的网络抖动。一般 TCP 阶段就能看出来,但偶尔会出现 TCP 握手时网络好、传输首字节时刚好赶上抖动。
排查方法是用 路由追踪工具 在用户出问题的时段抓一遍 MTR,看是不是某一跳出现 10%+ 丢包。
八、一个真实排查案例
用一个我们见过的真实案例串起来。某电商站点反馈"首页打开 3 秒",用户在广东电信。
用浏览器开发者工具看 Network 瀑布图:
| 阶段 | 实测耗时 | 评估 |
|---|---|---|
| DNS Lookup | 320ms | 病态 |
| Initial connection | 180ms | 警戒 |
| SSL | 410ms | 病态 |
| Request sent | 2ms | 正常 |
| Waiting (TTFB) | 1850ms | 病态 |
| Content Download | 240ms | 正常 |
3 个红灯,分别对应 3 个不同的问题。把这种"宏观慢"拆成"3 个具体问题",才能各个击破。
- DNS 320ms:查权威 DNS,发现用了某境外厂商,国内解析绕香港。换成 DNSPod 后降到 25ms
- SSL 410ms:Nginx 没配 OCSP Stapling 也没配 Session Cache,TLS 协议还是 1.2。开启 stapling、加 session cache、升级 OpenSSL 后降到 110ms
- Waiting 1850ms:开 PHP-FPM 慢日志,发现首页一个查询商品分类的 SQL 跑了 1.6 秒(没加索引)。加索引后降到 80ms
三处优化合计降低 TTFB 约 2500ms,整体页面首屏从 3 秒优化到 600ms。关键是先拆解再分析,不要看到"慢"就只盯着服务器。
九、TTFB 自检清单
下次再遇到 TTFB 高,按这个清单走一遍:
- 打开 Chrome 开发者工具 Network 面板,刷新页面,点击主请求看 Timing 标签
- 记下 6 个阶段的耗时,对照本文表 1 的健康/警戒/病态阈值
- 找出第一个超过警戒值的阶段,先处理它
- 处理完一个阶段,再次实测,验证下降到健康区间再继续下一个
- 不要同时改多个变量,否则不知道哪个优化生效了
需要多节点验证 TTFB 的话,可以用本站的 HTTP 测试工具,从全国多地实测你网站的完整 6 段耗时,定位是某地区独有问题还是全国性问题。
"TTFB"从来不是一个数字,而是 6 个数字的总和。把它拆开看,优化才会有方向。