为什么你的每次页面加载都在“等”TLS 握手?
我的处理经验
关于CDN TLS,最值得先弄清楚的是配置边界和排错顺序。打开一个 HTTPS 网站,你看到的内容并非瞬间呈现。在浏览器与服务器交换数据前,必须先完成一次“身份核查”和“钥匙交接”——这就是 TLS 握手。对于首次连接,握手需要 2 个网络往返(RTT),如果服务器距离用户很远,每个 RTT 可能耗时 50-200 毫秒,加起来就是 100-400 毫秒的纯等待时间。对于现代网页动辄几十个外部资源来说,这堆叠加起来会导致几秒的延迟。
好消息是,TLS 1.3 在 2018 年被标准化后,将握手从 2-RTT 压缩到 1-RTT,并且引入了一个更聪明的机制:会话恢复(Session Resumption)和预共享密钥(PSK)。当与同一个站点二次连接时,握手可以缩短到 0-RTT——也就是数据可以和握手请求一起发出,完全消除等待。而 CDN 边缘节点正是这项技术的绝佳施展舞台。
关联教程:此处可内链到“CDN TLS部署与验证”内容。
相关阅读:此处可内链到“CDN TLS常见问题”专题。
先拆开 TLS 1.3 的“黑盒”:握手到底在做什么?——CDN TLS
先看关键判断
无论哪种协议版本,TLS 握手的目标就两个:确认你连接的是真正的服务器(身份验证),以及协商一个只有你们俩知道的对称密钥(加密通信)。TLS 1.3 的精简之处在于,它把原本需要两次来回的密钥交换压缩到了单次:客户端直接发送自己的密钥份额和它支持的算法,服务器回应自己的密钥份额和证书,然后双方立即算出主密钥,后续所有数据都用这个主密钥加密。
但即便 1-RTT,对于高延迟链路(比如跨洲访问)依然是负担。于是“会话恢复”登场:第一次握手成功后,服务器会给客户端颁发一张“会话票据”(Session Ticket),其中包含一个加密后的主密钥。当客户端下次再连接同一个站点时,它可以直接出示这张票据,服务器解密后就知道“你还是你”,于是双方跳过完整的密钥协商,直接进入加密传输阶段。在 TLS 1.3 中,这甚至可以做到 0-RTT——客户端在发送票据的同时就把 HTTP 请求数据一起发过去,服务器验证通过立即响应。
PSK 就是那把“预配钥匙”与CDN TLS
容易忽略的细节
预共享密钥(PSK)是会话恢复的核心。其实质是:第一次握手结束后,客户端得到的不只是一个“会话 ID”或“票据”,而是一个对称密钥(PSK)。这个密钥结合了双方之前协商的各类参数。下一次连接时,客户端告诉服务器“我打算用 PSK 模式”,并附上对应的标识(Session ID 或 Session Ticket)。服务器根据标识找到之前存下的密钥,双方直接推导出新的通信密钥——整个过程不需要再次进行公钥交换。
PSK 模式在 TLS 1.3 中有两种应用:
- PSK-only:服务器仅凭 PSK 就完成身份验证(适用于之前已经做过完整验证的会话)。
- PSK + (EC)DHE:在 PSK 基础上额外进行一次临时密钥交换,提供“前向安全性”。即使 PSK 被泄露,历史会话仍然安全。这是全站 HTTPS 的推荐做法。
这两种方式都能实现 1-RTT 或 0-RTT 握手。其中 0-RTT 模式依赖客户端保存的“early data”功能,但需要注意重放攻击问题(后面会提到)。
延伸阅读:此处可内链到“CDN TLS配置案例”相关文章。
CDN 边缘节点如何“放大”会话恢复的好处?
CDN 的本质是在全球部署大量边缘节点,让用户“就近”接入。当用户请求一个域名时,DNS 或者 Anycast 会把用户引到最近的节点。如果这个节点第一次收到该用户的请求,它需要与源站完成完整的 TLS 握手吗?不需要——CDN 通常充当中间人,它会自己作为 TLS 终结端,直接与用户完成握手。
关键点在于:同一个用户如果短时间内多次请求同一个站点(比如加载页面上的各种图片、CSS、API 请求),CDN 边缘节点可以利用 TLS 1.3 会话恢复,甚至让浏览器复用同一个 TCP 连接(HTTP/2 多路复用)。但即使 TCP 连接断开,浏览器重新连接同一节点时,通过 Session Ticket 也可以实现 0-RTT 重启会话。
分布式会话恢复的挑战
CDN 有成千上万个边缘节点,用户可能在不同时间接入不同节点。如果用户第一次在北京节点握手并获得了 Session Ticket,第二次被路由到上海节点,上海节点没有那份 PSK,就无法恢复会话,只能重新完整握手。这意味着 CDN 必须做到节点间共享会话状态,或者采用确定性的票据加密方案。
成熟的 CDN 厂商通常采用以下策略:
- 全局票据加密:所有边缘节点共享一个对称加密密钥(或者是基于相同种子生成的密钥)。服务器签发 Session Ticket 时用这个密钥加密 PSK。任何节点收到票据后,都能用自己的密钥解密,从而恢复会话状态。这样节点无需同步数据库,完全无状态。
- 会话缓存同步(较少用):在节点本地内存中缓存会话数据,并通过分布式缓存(如 Redis)共享。但会增加延迟和复杂性。
- 优先保活 TCP 连接:结合 HTTP/2 或 HTTP/3 的多路复用,让浏览器在同一个节点上保持长连接,避免频繁重建 TLS 会话。
实际提速效果
在理想的 0-RTT 场景下,用户第二次请求的 TLS 握手时间为 0。实测数据(来自 Cloudflare 博客等技术资料)显示,启用 TLS 1.3 和会话恢复后,全球平均 HTTPS 连接建立时间可以从 200-300ms 降低到 50ms 以下,跨洋请求更是能从 500ms 降低到 100ms 以内。对于移动端用户(高延迟网络),体验改善尤其明显。
0-RTT 的重放风险与 CDN 的防护
我的处理经验
0-RTT 快速但危险:由于客户端发出的 early data 本质上没有绑定任何网络往返的随机数,攻击者可以截获并重新发送这份数据,造成重复操作(比如重复下订单)。所以 TLS 1.3 标准要求服务器端必须对 0-RTT 数据做幂等性处理——即重复接收相同请求不能产生副作用。CDN 在实现时通常会:
- 只允许 GET/HEAD 等幂等请求使用 0-RTT early data;
- 对非幂等请求(POST、PUT 等)强制降级到 1-RTT 或 2-RTT。
- 在边缘节点处添加请求去重机制(基于时间戳和随机数)。
所以在生产环境中,0-RTT 通常只用于资源加载、页面渲染等无副作用场景,交易类请求会走完整握手以保证安全性。
作为 CDN 用户,你需要做什么?
配置前的检查
如果你只是使用 CDN 服务,大多数主流 CDN 已经默认启用了 TLS 1.3 和会话恢复。你只需要确认:
- CDN 边缘节点支持 TLS 1.3(现在几乎全部支持)。
- 你的源站证书兼容 TLS 1.3(无需额外操作)。
- 如果对 0-RTT 有顾虑,可以在 CDN 控制台关闭 early data 功能(但会损失部分性能)。
如果你是自己搭建 CDN 或反向代理(比如 Nginx + OpenResty),需要检查配置:
ssl_protocols TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# 开启 Session Ticket
ssl_session_tickets on;
# 设置 Ticket 密钥(所有节点一致)
ssl_session_ticket_key /etc/ssl/ticket.key;
注意:密钥需要定期轮换,且所有节点使用相同的密钥文件,才能实现跨节点会话恢复。
总结:CDN + TLS 1.3 会话恢复 = 近乎零成本的性能提升
验证与回滚
对于绝大多数 Web 业务,TLS 1.3 的会话恢复和 PSK 握手是“零成本”的加速手段——它不消耗服务器额外资源,仅利用已有的握手密钥和客户端缓存,就能大幅削减二次连接的延迟。CDN 边缘节点凭借分布式架构和全局密钥共享,让这项技术发挥到极致:用户无论接入哪个节点,都能享受“秒开”体验。
你不需要成为密码学专家也能理解:就像去一家常去的咖啡店,店员认出了你,直接递上你常点的咖啡(0-RTT),而不是每次都重新核验身份证和支付密码。TLS 1.3 和 PSK 就是互联网版本的“老顾客卡”,而 CDN 就是分布在全球的连锁分店,让这张卡在所有门店都能使用。
如果你的网站还没有启用 TLS 1.3 或担心兼容性,请放心——主流浏览器和操作系统早已全面支持,是时候摆脱“握手慢”的困扰了。按这个顺序复查,CDN TLS遇到异常时也更容易定位。
延伸阅读
