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 头、关闭响应缓冲保证实时性、让各级超时与心跳节奏匹配。把这三点落实到配置和监控中,绝大多数“连上就断”“消息延迟”的问题都能定位并解决。上线前建议用真实心跳周期做一次超过十分钟的空闲连接测试,这比任何理论配置都更有说服力。
- 点赞
- 收藏
- 关注作者
评论(0)