迁移注意事项
哪些 1.1 知识作废、哪些照旧,以及升级 h2/h3 时真正会踩的坑。
哪些 1.1 知识作废
从 HTTP/1.1 专栏带过来的内容里,**只有第 1 部分(报文与成帧)**在 h2/h3 上失效:
| 作废的 | 替代 |
|---|---|
| 报文四段式结构、CRLF、obs-fold | 二进制帧 |
| request-line / status-line | 伪头字段 |
| 七步成帧判定链 | 帧长 + END_STREAM/FIN |
Transfer-Encoding、chunked | 帧层负责 |
Connection、Keep-Alive、Upgrade | ALPN + SETTINGS + GOAWAY |
| 流水线、逐跳字段概念 | 流多路复用 |
哪些照旧
第 2、3 部分逐字有效,三版通用:
升级协议不改变任何业务语义。ETag 还是那个 ETag,Cache-Control 还是那套规则。
应用层几乎不用改
对绝大多数应用,升级 h2/h3 是纯运维动作——终结点(nginx / CDN / 网关)改配置即可,应用代码不动。因为:
- 网关会自动完成 h2/h3 ↔ 1.1 的转换。
- 框架(Express、Spring、Go net/http)对上层暴露的是同一套请求/响应对象。
需要改代码的少数情况见下。
真正会踩的坑
1. 字段名大小写
h2/h3 强制小写。若代码里有:
if (req.headers['Content-Type'] === ...) // h2 下永远取不到必须改为小写查找。多数框架已归一化,但自己拼原始头或用底层库时要注意。
2. 依赖 1.1 特有字段的逻辑
if (req.headers.connection === 'keep-alive') // h2/h3 下该字段不存在
if (req.headers['transfer-encoding']) // 同上这些字段在 h2/h3 中被禁止出现,相关判断会静默失效。
3. 域名分片要撤掉
1.1 时代把静态资源拆到 img1.example.com、img2.example.com 以突破 6 连接限制。h2/h3 下这是负优化:每个域要单独握手、单独维护连接与压缩上下文。合并回单域。
同理,资源合并(雪碧图、打包成一个巨型 JS)的收益也大幅下降——多路复用后小文件不再昂贵,且细粒度更利于缓存命中。但也不必走极端拆成上千个小文件,请求本身仍有开销。
4. INITIAL_WINDOW_SIZE 默认值太小
h2 默认 65535 字节。跨洋 100ms RTT 下单流吞吐上限约 640 KB/s——大文件下载会明显变慢,且这个现象常被误判为「h2 比 1.1 慢」。
nginx 示例:
http2_body_preread_size 64k;更关键的是上游代理与应用服务器的窗口设置,需逐层检查。
5. MAX_CONCURRENT_STREAMS 必须设有限值
规范默认无限。不设置等于对流洪泛不设防(含 Rapid Reset CVE-2023-44487)。生产建议 100–128,并额外限制单位时间内的 RST_STREAM 速率。
6. 0-RTT 的重放风险
启用 h3 的 0-RTT 时,非幂等请求可能被重放。要么:
- 服务端对 0-RTT 中的非幂等请求返回
425 Too Early; - 要么实现请求去重(幂等键)。
不要在没有防护的情况下对写接口开 0-RTT。
7. UDP 必须可回落
h3 依赖 UDP/443 可达。必须同时保留 h2/1.1 端点,并确认防火墙放行 UDP 443。只开 h3 会让部分用户完全无法访问。
8. 可观测性要重建
QUIC 加密了传输层元数据,传统抓包分析(RTT、重传、窗口)失效。需要:
- 启用 qlog(端点导出的结构化传输日志)。
- 在应用层补齐指标:按协议版本分别统计延迟、错误率、连接复用率。
部署检查清单
- TLS 1.2+ 且启用 ALPN;h3 需 TLS 1.3。
- 证书链完整(h2 对 TLS 配置更严,禁用重协商与压缩)。
- 防火墙放行 UDP 443(h3)。
- 下发
Alt-Svc,并考虑配置 DNS HTTPS 记录以省掉首访的一跳。 -
MAX_CONCURRENT_STREAMS有限值 +RST_STREAM速率限制。 - 窗口大小按带宽延迟积调整。
-
MAX_HEADER_LIST_SIZE显式设置(防 HPACK 炸弹)。 - 撤销域名分片,收敛到单域。
- 保留 h2 与 1.1 回落路径并实测。
- 用
curl --http2/--http3与浏览器 DevTools 的 Protocol 列逐一验证。
验证命令:
curl -sI --http3 https://example.com | head -1curl -sI --http2 https://example.com | head -1收尾
三版协议的关系可以用一句话概括:
HTTP 语义从未改变,改的一直是「怎么把它装进字节」和「这些字节跑在什么之上」。
- HTTP/2 重写了装法(文本 → 二进制帧 + 多路复用 + 压缩)。
- HTTP/3 重写了载体(TCP → QUIC),顺带把流管理与流量控制下沉。
理解了这一点,HTTP/1.1 专栏里关于方法、状态码与字段的每一条规则,在 h2 与 h3 上都可以原样复用——那才是 HTTP 真正稳定的部分。