首页/安全/加密与证书速查/HelloRetryRequest(TLS 1.3 重试机制)
TLS 与 HTTPS60 条

HelloRetryRequest(TLS 1.3 重试机制)

当客户端 key_share 未覆盖服务端偏好的组时,服务端先回 HelloRetryRequest 要求换组,客户端再发一次 ClientHello,会额外增加一个半往返。

加密与证书速查安全更新于 2026-10-13

HRR 是什么

HelloRetryRequest(HRR)是 TLS 1.3 的协商重试机制。服务端读第一个 ClientHello 时,如果其中的 key_share 列表没有自己偏好的密钥交换组(例如客户端只带 x25519,而服务端策略要求 P-256),就回一条 HRR 指定要用的组,客户端据此再发一次 ClientHello。第二次 ClientHello 必须携带服务端指定的组,并复用 HRR 中的随机数置换值与 cookie,否则握手失败。代价是多一次往返,跨地域链路上会直接体现为首字节时间增加。

基本信息

触发条件首个 ClientHello 的 key_share 与服务端偏好组不匹配
代价握手多一个往返,1-RTT 变 2-RTT
协议要求第二次 ClientHello 使用 HRR 的随机数置换,并回带 cookie
观察方式抓包看同一连接是否出现两个 ClientHello,或用 s_client -msg
性质正常协商路径,不是错误
版本仅 TLS 1.3 存在该机制

典型使用场景

  • 建连延迟优化:发现大量连接触发 HRR 时,统一双方的密钥交换组配置。
  • 跨地域接入排查:海外用户首包慢,先确认是否被 HRR 多花了一个往返。
  • 客户端库评估:不同版本浏览器与 SDK 默认发送的 key_share 数量差别很大。
  • 中间设备兼容排查:设备改写 ClientHello 可能导致 HRR 后协商失败。
  • 服务端配置审计:只启用单一曲线的策略会放大 HRR 比例。

观察 HRR 是否发生

观察 HRR 是否发生
openssl s_client -connect example.com:443 -servername example.com -msg </dev/null 2>&1 | grep -ci 'hello retry'
# 抓包统计每个连接的 ClientHello 条数,大于 1 说明发生了 HRR
# tshark -r tls.pcap -Y 'tls.handshake.type == 1' -T fields -e tcp.stream | sort -n | uniq -c
# 服务端侧可用 SSLKEYLOGFILE + Wireshark 解密查看 key_share 协商细节

现象与处理

现象说明处理
同一连接出现两个 ClientHello首个未带服务端偏好的组客户端预置更多组,或服务端接受 x25519
握手成功但建连耗时增加多一次往返,RTT 越大越明显跨地域访问统一到 x25519 等通用组
HRR 之后握手失败cookie 未回带,或第二次仍未匹配核对客户端库版本,排查中间设备改写
HRR 比例接近 100%双方组配置互不兼容按服务端支持的组清单收敛客户端配置
少量 HRR 是正常现象,不需要处理;只有在高延迟链路上比例明显偏高、用户可感知时才值得统一密钥交换组配置。

易错点

  • 把 HRR 当握手失败排查:握手其实成功了,只是多一跳,白花时间找不存在的故障。
  • 忽略组配置不一致造成的长尾延迟:平均值看着正常,跨地域用户的建连体验却明显变差。
  • 为省掉 HRR 让服务端接受所有曲线:扩大攻击面与配置复杂度,收益往往不划算。
  • 只看客户端一侧日志:HRR 由服务端策略触发,需要两端配置一起核对。

数据来源与参考

  • RFC 8446 §4.1.4(HelloRetryRequest)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌