Nginx 反向代理 WebSocket:超时、缓冲与 Upgrade 头配置实战

举报
yd_282288291 发表于 2026/11/07 09:11:37 2026/11/07
【摘要】 从一次真实排查出发,讲清 Nginx 代理 WebSocket 必须透传的 Upgrade 头、关闭缓冲的原因,以及读写超时与心跳周期如何匹配,附完整配置与常见坑排查顺序。

Nginx 反向代理 WebSocket:超时、缓冲与 Upgrade 头配置实战

WebSocket 让浏览器与服务端保持长连接,但在生产环境中,它往往不是直接暴露给用户,而是放在 Nginx 后面。HTTP 反向代理的默认配置是为短连接请求设计的,直接套用到 WebSocket 上,最常见的结果就是连接建立后几十秒被断开,或者消息出现明显延迟。本文从一次真实排查出发,说明 WebSocket 代理需要额外关注的几个配置项。

一、为什么默认代理会断

Nginx 的 proxy_read_timeout 默认是 60 秒。它表示两次从上游读取数据之间的最大间隔,而不是连接的总时长。WebSocket 在没有业务消息时是静默的,60 秒内上游没有数据返回,Nginx 就认为后端无响应并主动关闭连接。前端表现通常是:页面开着不动,过一分钟左右提示连接断开,刷新后又恢复正常。

解决办法不是简单把超时调大,而是先想清楚业务的心跳频率,再让代理超时明显大于心跳间隔。例如客户端每 30 秒发送一次 ping,代理超时可以设为 75 秒,留出两次心跳的余量,避免网络抖动导致误判。

二、必须传递的 Upgrade 头

WebSocket 握手依赖 HTTP 的 Upgrade 机制。代理默认会使用 HTTP/1.0 与上游通信,并丢弃逐跳头(hop-by-hop headers),Upgrade 和 Connection 正在此列。因此需要显式设置:

location /ws/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

proxy_http_version 1.1 是前提,Upgrade 使用变量透传,Connection 固定为 upgrade。如果在同一个 location 里既要代理普通 HTTP 又要代理 WebSocket,可以用 map 动态判断:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

然后在 location 中使用 proxy_set_header Connection $connection_upgrade;,这样普通请求不会携带无意义的 upgrade 语义。

三、关闭缓冲,让消息实时到达

Nginx 默认开启响应缓冲,会把上游返回的数据攒到一定大小再发给客户端。对普通网页这是优化,对 WebSocket 却是灾难:小消息可能被延迟到缓冲区达到阈值才发送,表现为“消息发了但对方很久才收到”。

需要关闭缓冲:

proxy_buffering off;
proxy_cache off;

如果使用的是较新的 Nginx,还可以关注 proxy_socket_keepalive,让 Nginx 与上游之间的连接保持复用,减少频繁握手。但要注意,WebSocket 连接本身是长连接,keepalive 更多影响的是普通请求,不要把它和 WebSocket 的超时混为一谈。

四、超时参数要成组设置

除了 proxy_read_timeout,还有 proxy_send_timeout 和 proxy_connect_timeout。连接建立阶段应当快速失败,通常 5 到 10 秒足够;读写超时则要覆盖业务最长静默期。一个可用的组合是:

proxy_connect_timeout 10s;
proxy_send_timeout 75s;
proxy_read_timeout 75s;
send_timeout 75s;

这里 send_timeout 是 Nginx 向客户端发送数据的超时,容易被忽略。如果客户端网络较差,发送阻塞同样会触发断开,因此它也应与心跳周期匹配。

五、常见坑与排查顺序

第一,确认请求真的走了 WebSocket 的 location。可以用浏览器开发者工具的 Network 面板查看握手请求的响应码是否为 101,以及响应头里是否包含 Upgrade: websocket。如果返回 200,说明请求被普通 HTTP 逻辑处理了。

第二,检查是否有多层代理。CDN、负载均衡、Ingress 各自都可能有独立的超时和缓冲配置,只改 Nginx 往往不够。排查时应逐层确认,从最外层开始记录每一跳的超时值。

第三,注意 proxy_set_header Connection 被重复设置。多个 location 或 include 文件中如果都设置了该头,后出现的配置可能覆盖前面的,导致握手失败。建议全站统一用 map 管理。

第四,压力测试时不要只测连接数,还要测静默连接。大量空闲连接同时存在时,超时配置不合理会集中触发断开,形成雪崩式的重连风暴。

六、小结

WebSocket 反向代理的核心不是某一个神奇参数,而是三件事:用 HTTP/1.1 正确透传 Upgrade 头、关闭响应缓冲保证实时性、让各级超时与心跳节奏匹配。把这三点落实到配置和监控中,绝大多数“连上就断”“消息延迟”的问题都能定位并解决。上线前建议用真实心跳周期做一次超过十分钟的空闲连接测试,这比任何理论配置都更有说服力。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。