综合 nginx 1.31.0 拒收 h2/h3 里的 Connection、Keep-Alive:先排查前面那台 1.1 网关

2026-11-12 21:04:23

nginx 1.31.0 拒收 h2/h3 里的 Connection、Keep-Alive:先排查前面那台 1.1 网关

nginx.org 的 CHANGES 记录 1.31.0(2026-05-13)已经发布。这一版里最容易被忽略、但线上影响最直接的一条,是对 HTTP/2、HTTP/3 请求头的强制校验。

官方变更记录:nginx.org/en/CHANGES | nginx.org/2026.html

1.31.0 改了什么

原文变更条目:

  • Security:使用 proxy_set_body 指令时存在安全问题(CVE-2026-42926)。CHANGES 中该段描述不完整,受影响范围与修复版本以 nginx 安全公告为准。
  • Change:nginx 现在会拒绝携带 Connection、Proxy-Connection、Keep-Alive、Transfer-Encoding、Upgrade 头行的 HTTP/2 与 HTTP/3 请求,以及 TE 值不是 trailers 的请求。
  • Change:ngx_http_dav_module 拒绝源与目标相同、或两者存在父子集合关系的 COPY / MOVE 请求。
  • Change:invalid alert、record layer failure 以及 SSL alert number N(任意 alert 编号)的日志级别从 crit 降为 info。
  • Change:sticky 模块可用 --without-http_upstream_sticky_module 关闭;旧的 --without-http_upstream_sticky 已弃用。
  • Feature:新增 ngx_http_tunnel_module;代理认证支持 auth_basic、satisfy、auth_delay。
  • Feature:upstream 块内支持 least_time 指令。
  • Feature:stream 模块支持 proxy_ssl_alpn。
  • Bugfix:使用 proxy_set_body 或 proxy_pass_request_body 时,到 HTTP/2 后端的连接可能不被缓存。
  • Bugfix:代理 HTTP/0.9、SCGI、uWSGI 响应时,如果首行未被完整读取,响应可能传输错误。

以前能通过、现在会失败的原因

HTTP/1.1 里,Connection、Keep-Alive、Transfer-Encoding、Upgrade、TE、Proxy-Connection 都是逐跳(hop-by-hop)头,只对当前这一跳有意义。Connection: keep-alive 决定这条 TCP 连接怎么复用,Transfer-Encoding: chunked 描述这一段报文怎么分帧。到了 HTTP/2,这些语义全部由帧层承担:连接复用靠 SETTINGS,消息边界靠 END_STREAM 和 DATA 帧长度,协议升级靠 ALPN 在握手阶段完成。

RFC 9113 §8.2.2 因此直接规定:Connection、Proxy-Connection、Keep-Alive、Transfer-Encoding、Upgrade 出现在 HTTP/2 请求里即视为 malformed,TE 只允许取值 trailers。RFC 9114 对 HTTP/3 沿用同一规则。

1.31.0 之前,nginx 对 h2/h3 请求里的这些字段没有做这项强制校验:HEADERS 帧照常解析,字段被忽略,或者通过 $http_* 变量暴露给配置,请求继续进入 location 处理。所以一个把 HTTP/1.1 header 表原样塞进 h2 的客户端,跑了很久也不会报错,问题是潜伏的。

1.31.0 起,按 RFC 判为 malformed 的请求会被直接拒绝。客户端侧通常表现为 RST_STREAM,或者收到 4xx 响应;对应的 error_log 文案、确切的响应码与是否可配置,需要看安全公告和实测结果。

三类高概率会触发的链路

把 1.1 头表直接复用到 h2 的自研 SDK / RPC 框架。 典型写法是维护一份默认 header map(Connection: keep-alive、Keep-Alive: timeout=60、需要上传时再加 Transfer-Encoding: chunked),换协议时只换了传输层,header 构造逻辑没动。这类客户端此前一直“能跑”,升级后会被拦截。

前面还有 HTTP/1.1 网关的链路。 边缘网关收 1.1,回源时用 h2,转发时把客户端原始 header 直接透传,只在末尾追加几个自定义头。这是最常见的一种:出问题的不是终端客户端,而是中间那一跳。nginx 自己在 1.29.7 起默认不再向上游发送 Connection 代理头,但自研网关、老版本封装未必做了同样的清理。

在已建立的 h2 连接里再发 Upgrade: h2c,或发送 TE: gzip 之类的客户端。 前者是 h2 里套用 HTTP/1.1 升级语义,后者是 TE 取值不是 trailers,两者都落在新校验范围里。

具体到某个语言或某个库是否受影响,用自己的流量判断更可靠,不要依据版本号猜测。

复现与排查

先确认入站 h2/h3 请求里到底带了什么。抓包看 HEADERS 帧最直接:

tcpdump -i any -s 0 -w h2.pcap 'tcp port 443'

在 Wireshark 里按 http2.headers 过滤,逐条检查是否含上述六个字段。

用 curl 构造请求时要注意,部分版本在 h2 模式下会屏蔽或改写 Connection 一类头,构造出来未必是你要验证的报文:

curl --http2 -sv \
-H 'Connection: keep-alive' \
-H 'Keep-Alive: timeout=5' \
https://example.com/ -o /dev/null

发不出去就用 nghttp2 的客户端,或者直接拿业务 SDK 打预发环境。抓包工具的 header 构造行为以各自文档为准。

预发环境把日志开到 info,方便对齐时间点:

error_log /var/log/nginx/error.log info;

如果链路上 nginx 自己是回源方,检查历史配置里有没有显式写过代理头:

location /api/ {
proxy_pass https://backend;
# 1.29.7 起 nginx 默认不再向上游发 Connection;
# 历史配置若显式设过,这里清掉
proxy_set_header Connection "";
}

升级检查清单

  1. 抓一遍现网 h2/h3 入站流量,统计带 Connection、Proxy-Connection、Keep-Alive、Transfer-Encoding、Upgrade、非 trailers 的 TE 的请求占比,以及来源 IP / UA。
  2. 逐个检查 HTTP/1.1 → h2 的网关转发实现,确认它是否透传原始 header。
  3. 检查自研 SDK 的 header 构造逻辑,特别是“默认 header 表 + 复用”的写法。
  4. 灰度时先上边缘层,观察被拒请求的 error_log 与客户端错误率。
  5. 业务侧若有依赖 $http_connection、$http_keep_alive 取值的配置,确认新版本下的行为。
  6. DAV 的 COPY / MOVE 自动化脚本:同源复制、父子目录整体移动会被拒,提前改脚本。
  7. 日志告警规则:SSL alert 与 record layer failure 从 crit 降到 info,原先挂在 crit 上的告警会静默失效。
  8. 打包脚本里用到 --without-http_upstream_sticky 的,换成 --without-http_upstream_sticky_module。
  9. 涉及 proxy_set_body / proxy_pass_request_body 且后端是 h2 的,验证连接缓存行为是否变化。
  10. 准备回滚预案,并确认 CVE-2026-42926 的修复细节后再决定上线窗口。

相邻版本背景

  • 1.29.6(2026-03-10):sticky 会话保持开源;upstream 块支持 sticky 指令,server 支持 route / drain。
  • 1.29.7(2026-03-24):listen 支持 multipath(MPTCP);upstream keepalive 默认启用,proxy_http_version 默认 1.1,默认不再发送 Connection 代理头。
  • 1.29.8(2026-04-07):max_headers 指令;OpenSSL 4.0 兼容。
  • 1.30.0(2026-04-14):进入 stable 分支。
  • 1.29.4(2025-12-09):proxy 模块支持 HTTP/2;ECH(ssl_ech_file);chunked 编码里的裸 LF 视为错误(400)。

1.29.7 已经在客户端侧做了清理,1.31.0 把服务端侧的校验补齐,两条改动方向是一致的。链路上只要有一环还是老逻辑,就会在 1.31.0 上暴露出来。

链接

  • 变更记录:
  • 2026 年发布公告:
  • 协议依据:RFC 9113 §8.2.2(HTTP/2 连接专用头字段),RFC 9114(HTTP/3)沿用同一规则

CVE-2026-42926 的触发条件、影响版本与是否有临时缓解手段,以 nginx 安全公告为准。

复制全文 生成海报 nginx HTTP 2 运维 反向代理

推荐文章

程序员茄子在线接单