Nginx 作为反向代理回源时,如果上游服务使用 HTTP/2,有时会在日志中看到 upstream prematurely closed connection 或者 upstream reset stream,抓包进一步显示 HTTP/2 RST_STREAM 帧。这个帧不会像 TCP RST 那样直接断开整个连接,而是只终止当前请求对应的流,其他多路复用的请求理论上不受影响,但 Nginx 仍然会把当前请求判定为失败。定位这类问题需要先确认 RST_STREAM 是从哪一端发出、携带什么错误码,再结合 Nginx 的超时配置和上游服务行为做调整。

一、先分清 RST_STREAM 发生在哪一段连接
完整链路通常包含两个独立连接:客户端到 Nginx 的连接,以及 Nginx 到上游服务的回源连接。客户端可能使用 HTTP/1.1 或 HTTP/2,Nginx 回源则可能由配置决定使用 HTTP/1.1 或 HTTP/2。日志中看到的 RST_STREAM 如果与回源相关,一般说明 Nginx 作为 HTTP/2 客户端,收到了上游服务发送的 RST_STREAM 帧。此时要检查 Nginx 的 error.log 是否出现 upstream prematurely closed connection while reading response header from upstream 或 sendfile() failed 等记录。
确认这一点最直接的方式是抓包。可以在 Nginx 所在的机器上对回源网卡抓取 TCP 流量,再过滤出 HTTP/2 帧。例如使用 tcpdump 保存流量,再用 Wireshark 过滤 http2.type == 3,因为 RST_STREAM 帧类型值为 0x3。抓包中需要关注帧头中的 Stream Identifier 和 Error Code 字段。Error Code 为 0x0 表示 NO_ERROR,通常是正常取消;0x8 表示 CANCEL,说明上游主动取消了流;0x1 表示 PROTOCOL_ERROR,则可能涉及 HTTP/2 协议实现问题或头部格式错误。
# 抓取 443 端口的回源流量并保存为文件 tcpdump -i any -s 0 -w /tmp/nginx_upstream.pcap 'tcp port 443' # 用 tshark 直接过滤 RST_STREAM 帧 tshark -r /tmp/nginx_upstream.pcap -Y 'http2.type == 3' \ -T fields -e frame.time -e tcp.stream -e http2.streamid -e http2.error_code
如果抓包确认 RST_STREAM 确实来自上游服务器的 IP,并且错误码为 CANCEL 或 INTERNAL_ERROR,就可以把排查重点放到上游服务本身。如果错误码为 REFUSED_STREAM,通常意味着上游在当前连接上已经达到了最大并发流限制,此时 Nginx 可以尝试减少连接复用或改用 HTTP/1.1 回源。
二、常见触发原因与日志特征
上游服务主动发送 RST_STREAM 的原因很多,但结合日志特征可以缩小范围。第一种是应用层主动取消请求。比如上游服务在收到请求后,内部逻辑判断请求不合法或需要重定向,直接调用 HTTP/2 的 cancel 接口终止了流。此时 Nginx 的日志通常会在 upstream_response_time 非常短的情况下出现 502 或 504,同时 upstream_status 为空,因为响应头尚未返回。
第二种是上游处理超时。HTTP/2 连接中,如果上游应用处理时间超过自身的超时阈值,应用可能主动发送 RST_STREAM 来终止当前请求,而不会返回 HTTP 504 响应。Nginx 侧的典型表现是 proxy_read_timeout 还没到,但请求已经被重置。此时如果单独增大 Nginx 的 proxy_read_timeout 往往无效,因为上游应用仍在按自己的超时时间执行。需要同时调整上游服务的请求处理超时或异步处理策略。
第三种是 HTTP/2 流控限制。HTTP/2 有流控窗口机制,如果上游服务没有及时更新窗口,或者 Nginx 发送请求体过快,可能触发对端发送 RST_STREAM。这种情况在 POST 大量数据时更容易出现。抓包时可以观察 WINDOW_UPDATE 帧的数量和窗口大小,如果请求发送过程中长期没有窗口更新,大概率就是流控相关。
第四种是上游 HTTP/2 实现缺陷。部分后端框架或语言库对 HTTP/2 的实现不够成熟,在长连接、TLS 重协商或某些头部组合下会错误地重置流。这种问题通常随机出现,且错误码多为 INTERNAL_ERROR 或 PROTOCOL_ERROR。可以尝试升级上游服务的 HTTP/2 库,或者在 Nginx 中临时改为 HTTP/1.1 回源做对比测试。
三、从 Nginx 侧调整参数与验证方法
如果暂时无法修改上游服务,可以优先在 Nginx 侧做配置调整。对于回源使用 HTTP/2 的场景,需要重点关注连接复用和超时设置。Nginx 的 proxy_http_version 2.0 指令可以启用 HTTP/2 回源,但 HTTP/2 回源对上游连接的复用方式与 HTTP/1.1 不同,单条 TCP 连接上承载多个流,任何一个流被 RST_STREAM 都只影响对应请求。配置示例如下:
location /api/ {
proxy_pass https://backend;
# 使用 HTTP/2 回源
proxy_http_version 2.0;
# 回源超时时间
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# 故障重试条件
proxy_next_upstream error timeout http_502 http_503 http_504;
}
需要特别说明的是,当使用 HTTP/2 回源时,不能像 HTTP/1.1 那样通过 proxy_set_header Connection "" 来维持长连接,因为 HTTP/2 本身没有 Connection 头部的概念。如果配置中仍保留该指令,可能导致 Nginx 与上游的协议协商异常。可以删除该指令,并确保上游服务器支持 ALPN 协商 h2。
如果怀疑上游 HTTP/2 实现不稳定,可以切换回 HTTP/1.1 回源,并配合连接池复用。这样虽然失去了多路复用的优势,但故障隔离性更好,上游单个连接的错误不会影响其他请求。配置如下:
location /api/ {
proxy_pass https://backend;
# 回源使用 HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
# 启用上游长连接
upstream backend {
server 192.168.10.20:443;
keepalive 32;
}
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
调整之后需要验证问题是否消失。可以通过压测工具模拟并发请求,观察 error.log 中是否还出现 RST_STREAM 相关记录。同时可以在 Nginx 中临时打开 debug 日志,但生产环境不建议长时间开启,因为 debug 日志量很大。使用 error_log /var/log/nginx/debug.log debug; 后,重新加载配置并复现问题,日志中会包含更详细的 HTTP/2 帧收发信息,包括帧类型、流 ID 和错误码,有助于进一步确认。
四、回源 HTTP/2 的可靠性考量与替代方案
HTTP/2 回源带来多路复用、头部压缩等好处,在流量较大且上游服务支持良好的情况下,可以减少 TCP 连接数量,降低握手开销。但它的复杂度也更高,单条连接上的多路复用意味着一个连接级别的网络故障或中间设备异常会同时影响所有流。相比之下,HTTP/1.1 回源配合 Nginx 的 keepalive 连接池,虽然需要维护更多 TCP 连接,但每个请求的故障隔离更明确,排障路径更短。
如果业务对延迟不是极度敏感,且回源链路中存在不支持 HTTP/2 的负载均衡设备、防火墙或老旧代理,优先选择 HTTP/1.1 回源会更稳定。很多生产环境在 Nginx 与上游之间经过多层网络设备时,HTTP/2 帧可能被中间设备误处理,导致 RST_STREAM 频繁出现。此时可以通过抓包看 RST_STREAM 是否都来自上游的真实 IP,如果来自中间设备的 IP,则需要检查网络链路上的设备策略。
最终处理方案通常分成三步:先用抓包和 debug 日志确定 RST_STREAM 的方向和错误码;再根据错误码调整 Nginx 的超时、并发和回源协议;如果上游服务实现缺陷无法快速修复,就回退到 HTTP/1.1 回源并观察一段时间。连接重置类问题不是 Nginx 单方面能够完全解决的,但通过清晰的排查路径,可以把影响控制在可控范围,避免因为回源不稳定导致用户请求大量失败。
Nginx回源HTTP/2 RST_STREAM连接重置修改时间:2026-09-23 06:40:18