HTTPS 建立连接的耗时里,TCP 握手 + TLS 握手是大头。TLS 1.2 需要额外的 2 个 RTT 才能发第一个 HTTP 字节,TLS 1.3 减到 1 个,会话恢复场景 0 个。它是怎么省的?
1.2 的流程为什么慢
Client Server
| ---- ClientHello ------------> | 1-RTT 开始
| <--- ServerHello + 证书 + Done - |
| ---- 密钥交换 + Finished -----> | 2-RTT 开始
| <--- Finished ----------------- |
| ==== 终于可以发 HTTP 了 ======== |
1.2 里客户端要先“看一眼”服务器选了什么套件,才能开始算密钥——协商在前,密钥交换在后,一来一回就多出来了。
1.3 的改动:把猜测变成标配
1.3 直接砍掉了那堆老旧套件,只留下 ECDHE 一族。既然算法没悬念,客户端第一次打招呼就能把密钥交换材料(key_share)带上:
Client Server
| -- ClientHello + key_share --> |
| <--- ServerHello + 证书 + Finished -- | 服务器已能算出密钥
| ---- Finished + [应用数据] ---> | 客户端也能发数据了
| ==== 1-RTT 后双向应用数据 ===== |
第二个 RTT 里客户端就能携带 HTTP 请求,握手指纹也更干净——套件少了,中间盒想搞“降级劫持”的可乘之机也少了。同时 1.3 对整个 ServerHello 之后的握手做了加密,抓包里再也看不到明文的证书链,排查问题时记得用 SSLKEYLOGFILE 配 Wireshark。
0-RTT:再快一步的代价
会话恢复时,客户端凭上次的 PSK 在第一个飞行里直接带上应用数据(early data),真正零往返。代价是这些数据可能被重放——所以 0-RTT 数据只能用于幂等请求(GET 拉取没问题),下单、支付这类写操作必须等握手完成。Nginx 里对应 ssl_early_data on;,打开前先想想自己的接口幂等吗。
验证一把
openssl s_client -connect example.com:443 -tls1_3 < /dev/null 2>/dev/null | grep -E "Protocol|Cipher"
如今主流浏览器和 CDN 默认都协商 1.3。对前端性能而言,这一 RTT 省在每一次新建连接上,配合 TCP Fast Open 和连接复用,冷启动体验差距非常直观。