nginx代理websocket稳定需三要素:心跳间隔严格小于proxy_read_timeout(建议25–30秒),客户端发"ping"、服务端轻量响应"pong"并防抖,且nginx必须透传upgrade/connection头、禁用buffering。

要让 Nginx 代理下的 WebSocket 连接稳定不掉线,关键不在“多发心跳”,而在于让 Ping/Pong 与 Nginx 超时机制严格对齐、避免无效探测、并把资源消耗压到最低。
确保心跳间隔小于 proxy_read_timeout
Nginx 默认的 proxy_read_timeout 是 60 秒,只要客户端或服务端超过这个时间没发任何数据(包括 pong),Nginx 就会主动断开连接。所以:
- 应用层心跳(如客户端每 30 秒发 "ping")必须严格小于该值,建议设为 25–30 秒
- 若后端主动发 ping,间隔应 ≤ 25 秒;同时把
proxy_read_timeout设为 ≥ 45 秒,留出 pong 响应和网络抖动余量 - 不要只调大超时参数来“掩盖”心跳问题——超时设太大反而延迟暴露真实断连
用客户端发 ping + 服务端轻量响应,替代全量广播
后端每秒向所有连接发 ping,是高并发场景下最典型的 CPU 和带宽浪费源。更优做法是:
- 客户端定时发送纯字符串
"ping"(不走 JSON 解析,避免混淆业务消息) - 服务端收到后仅做字面量判断:
if (data === 'ping') ws.send('pong'),不查库、不记录、不触发业务逻辑 - 同一连接在 10 秒窗口内重复收到 ping,只响应第一个,其余直接丢弃(防抖)
- 服务端不维护心跳计时器,只依赖客户端行为和连接关闭事件清理资源
配置 Nginx 正确透传 Upgrade 和 Connection 头
如果心跳通但连接频繁闪断,大概率是协议升级没走通。必须确认以下配置已启用:
-
proxy_http_version 1.1—— WebSocket 必须基于 HTTP/1.1 -
proxy_set_header Upgrade $http_upgrade—— 透传 Upgrade 头,值通常为websocket -
proxy_set_header Connection $connection_upgrade—— 使用map指令动态设置,避免硬编码"upgrade" - 禁用
proxy_buffering,防止 Nginx 缓存 pong 帧导致延迟
前端需实现双向确认与假死检测
浏览器无法发送协议级 ping 帧,所以应用层心跳必须补足这一缺失:
- 发 ping 后启动
pongTimeout计时器(建议为心跳间隔的 1.5 倍,如 45 秒) - 收到
"pong"字符串立即重置计时器,并更新lastPongTime - 若超时未收到 pong,且
readyState === OPEN,就主动close()并触发重连,不等 onclose 被动回调 - 每次收到任意业务消息也重置心跳计时器——有数据收发就不额外发 ping,省资源











