首字节时间的大头往往不在服务器上。一次冷启动访问要依次付出 DNS 解析、TCP 握手、TLS 握手、服务端处理、首包传输五段开销,其中前三段完全由网络距离与解析配置决定,你优化再多的后端代码也改不了。正确的加速顺序是:先压解析与建连,再提高边缘缓存命中率,最后才考虑协议升级。
先把 TTFB 拆开看
| 阶段 | 典型量级 | 主要影响因素 | 优化手段 |
|---|---|---|---|
| DNS 解析 | 20–200ms | 递归深度、TTL、线路质量 | 多线路解析、减少解析层级 |
| TCP 握手 | 1×RTT | 物理距离 | 就近接入、边缘节点 |
| TLS 握手 | 1–2×RTT | 协议版本、会话复用 | TLS 1.3、session 复用、OCSP stapling |
| 服务端处理 | 10ms–数秒 | 应用逻辑、数据库 | 缓存、查询优化、异步化 |
| 首包传输 | 1×RTT | 带宽、拥塞窗口 | HTTP/2、HTTP/3、压缩首屏体积 |
注意 RTT 是物理量:跨省 20 到 40 毫秒,跨国 150 到 250 毫秒,应用层改不了。所以在 RTT 大的链路上,减少往返次数的收益远大于把服务端逻辑优化 10 毫秒。
如果 DNS 解析要 150ms、TLS 握手要两个 RTT,那么无论后端优化到什么程度,用户感知的首屏时间都不会好看。先治网络往返,再治服务端。
DNS 优化:先把查询次数降下来
多线路解析是性价比最高的一步:按运营商或地域返回不同的 A 记录,让电信用户解析到电信入口、联通用户解析到联通入口,能省掉跨网互联那几十毫秒。
TTL 是个权衡:设得短(60 秒以内)便于故障切换和灰度,但每个新访客都要重新解析,递归缓存命中率下降;设得长(1 小时以上)解析更快更稳,代价是切流量时要等缓存过期。常见做法是平时 300 到 600 秒,变更前几小时把 TTL 降到 60 秒,切换完成后再调回来。
CNAME 扁平化针对的是解析链路长度。业务域名 CNAME 到 CDN 厂商的域名,厂商再 CNAME 一层,递归解析就要多走几跳。部分 DNS 服务商支持在权威侧直接返回 A 记录(俗称 CNAME 拉平),把多级 CNAME 压成一次查询。
DoH 与 DoT 解决的是明文解析被劫持与隐私问题,对速度通常是负收益:多一次 TLS 握手,还绕过了运营商的就近递归缓存,可能解析到更远的节点。给终端用户配 DoH 前先确认它不是新瓶颈。
调度方式与回源形态
调度决定「用户连哪个边缘节点」,回源决定「边缘节点怎么取内容」,两件事不能混谈。常见的接入形态有三种:
- DNS 调度:权威 DNS 根据解析来源返回最近的边缘 IP。绝大多数 CDN 的默认方式,兼容性最好,缺点是受本地 DNS 缓存影响,调度粒度粗。
- 302 调度:通用入口先接请求,再 302 跳到最优节点。精度更高、能绕开 DNS 缓存,代价是多一次往返,首字节时间反而更长,一般只用于大文件下载。
- 边缘计算:在边缘节点跑一小段逻辑(鉴权、改写、AB 测试、简单聚合),把本该回源的请求挡在边缘。收益最大,但要求业务能拆出一部分无状态逻辑,属于云原生改造的一部分,可以对照 云原生架构入门 看整体路径。
回源协议怎么选
HTTPS 回源意味着边缘到源站也要握手,多一到两个 RTT,但换来三件事:内容与证书不被中间链路窥探、能靠证书校验源站身份防止回源被劫持、满足全链路加密的合规要求。折中做法是回源仍走 HTTPS,但开启长连接与 TLS 会话复用摊销握手成本,源站侧细节可以看 Nginx 性能调优清单。
回源用 HTTP 时,源站看到的协议是 http,若不处理,应用生成的绝对 URL、跳转地址、Cookie 的 Secure 标记都会出错。通行约定是让边缘回源时带上 X-Forwarded-Proto: https 与原 Host,源站据此判断真实协议。源站必须只信任来自 CDN 回源段 IP 的这个头,其余来源一律忽略,否则客户端可以自己伪造。
缓存键决定命中率
命中率上不去,CDN 就只是个更贵的透明代理,回源流量原样压在源站上,账单也不会降,回源与计费的关系见 云服务器带宽与流量计费。三个常见失手点:
- 无意义的 query 参数:
?utm_source=、?_t=时间戳会被算进缓存键,让同一个页面碎成上千个副本。要么在边缘配置里忽略指定参数,要么在应用侧把跟踪参数改成纯前端读取。 - Vary 头:
Vary: User-Agent几乎等于关闭缓存,UA 组合是海量的。正确做法是在边缘按设备类型归一成少数几类,或者干脆用响应式页面只缓存一份。 - 缓存指令缺失或写成 private:源站没给明确指令时,CDN 往往不敢缓存。动态页面也可以缓存,只要给出
s-maxage与stale-while-revalidate,让边缘在后台异步回源刷新。
什么内容不该上 CDN
带 Set-Cookie 的响应不要缓存,否则会把 A 用户的会话发给 B 用户;登录态接口就标 Cache-Control: private, no-store。强个性化页面(我的订单、购物车、后台)同理,正确做法是把个性化片段做成边缘不缓存的接口,页面骨架照常缓存。实时性要求到秒级的数据(库存、价格、抢购结果)也不适合边缘缓存,除非能接受短暂的不一致。内网服务与访问量极低的长尾域名,接入运维成本可能高于收益。纯静态资源更适合放进对象存储再挂 CDN,选型看 对象存储对比。
HTTP/2 与 HTTP/3 的真实收益
HTTP/2 的多路复用把同域名下的并发请求压到一条连接上,省掉额外的握手,首屏带几十个静态资源的页面通常能快 10% 到 30%。代价是服务端要维持连接状态、TCP 层一丢包会阻塞所有流,且必须全程 HTTPS。
HTTP/3 基于 QUIC,把传输层搬到 UDP,丢包只影响单个流,弱网与移动网络改善最明显,建连还能 0-RTT 恢复。代价是部分企业网络与老旧设备会阻断 UDP,必须保留 TCP 回退,且边缘侧每连接 CPU 开销上升。落地顺序是先上 HTTP/2,看真实用户 RTT 分布,再对弱网占比高的业务开 HTTP/3。
常见问题
TTL 设得越短,故障切换越快,是不是都该设短?
不该。TTL 短意味着每个新访客都要重新走一遍递归解析,解析缓存命中率下降,整体首字节时间变长。合理做法是平常 300 到 600 秒,在计划内的变更或切换前提前降到 60 秒,变更完成后再调回去,把灵活性和稳定性分开处理。
CDN 缓存命中率一直上不去,从哪查起?
先按 URL 维度看回源明细,重点是带 query 的请求占比与 Vary 头的配置。多数情况是无意义参数把缓存键打碎了,其次是源站没给明确的 Cache-Control 导致边缘不敢缓存。排除这两点之后,再看是不是命中的都是登录态或强个性化接口,那些本来就不该缓存。
开了 CDN 之后源站日志里全是边缘节点 IP,怎么拿到真实访客 IP?
让边缘回源时携带 X-Forwarded-For 与 X-Real-IP,源站用 realip 模块或应用层中间件读取。关键是只对 CDN 回源段的 IP 采信这些头,其他来源一律覆盖,否则任何人都能伪造访客地址,日志与风控数据都会失真。
HTTP/3 开了之后部分用户访问不了,怎么办?
这通常是中间网络封了 UDP 443。正确的部署方式是同时保留 HTTP/2 的 TCP 回退,并通过 Alt-Svc 头宣告 HTTP/3 可用,让客户端自行降级;不要直接把源站切到只支持 QUIC。上线后按地域与运营商维度对比失败率,再决定是否扩大范围。
延伸阅读
- Nginx 性能调优清单:worker、连接数与缓存怎么设:回源那一段的延迟由源站决定,这篇把相关参数讲透了。
- 云服务器带宽与流量计费:缓存命中率直接影响回源流量,也就直接影响月底账单。
- 对象存储怎么选:OSS、S3 与自建 MinIO 对比:静态资源更适合放对象存储再挂 CDN,这篇给了对比维度。
- 云原生架构入门:微服务、容器与 CI/CD 怎么落地:边缘计算属于同一套工程体系,可以接着往下看。
- 服务器监控告警怎么搭:从指标采集到告警收敛:首字节时间的改善要用真实指标验证,别只看单次 curl。