WebSocket频繁断开主因是心跳与Nginx超时参数共振,需协同配置:心跳间隔避开60秒倍数(推荐25/45/55秒),proxy_read_timeout须大于心跳间隔并留余量(建议300–86400秒),心跳帧必须真实经Nginx转发,同时启用TCP保活机制。

WebSocket连接在Nginx后频繁断开,本质是“静默连接”撞上了多层超时关卡。单纯调大Nginx超时值或只加心跳都不够稳妥——必须让两者协同工作:心跳包要能被Nginx“看见”,且不能触发它的超时共振;Nginx参数则要为心跳留出明确、安全的缓冲空间。
心跳间隔必须避开60秒整数倍
Nginx默认proxy_read_timeout为60秒,若心跳恰好每60秒发一次,极易与Nginx内部计时器产生周期性共振,导致连接在第60秒末被误判为“空闲”而切断。这不是偶发,而是确定性风险。
- 推荐心跳间隔设为25秒、30秒、45秒或55秒——这些值既足够频繁,又错开60秒倍数
- 避免使用30秒(看似合理)但需确认后端是否也用30秒:若服务端主动ping+客户端pong响应耗时接近1秒,实际空窗可能逼近31秒,仍存在踩线风险
- B站等高并发场景常用15秒心跳,兼顾稳定性与资源开销
Nginx超时参数要大于心跳间隔并保留余量
proxy_read_timeout不是“心跳周期上限”,而是“允许的最大无数据时间”。它必须严格大于心跳间隔,并预留网络抖动和处理延迟的空间。
- 若心跳设为30秒,
proxy_read_timeout至少设为60秒(2倍),生产环境建议300–86400秒(5分钟–24小时) -
proxy_send_timeout同样需≥心跳间隔,防止服务端发送pong响应超时被中断 - 不要盲目设为7天(604800秒):虽可保连接不断,但会阻碍异常连接及时释放,增加内存与文件描述符压力
确保心跳帧真实流经Nginx代理链路
有些实现把心跳做成服务端内部定时器+内存状态刷新,不走WebSocket通道——这对Nginx完全无效。心跳必须是真正的二进制或文本帧,经由TCP传输,穿过Nginx转发。
- 前端用
ws.send("ping")或ws.ping()(如支持)发送,服务端收到后立即回pong或业务确认帧 - 禁用“伪心跳”:例如仅更新Redis连接状态、写日志、或调用本地方法却不发网络包
- 验证方式:在Nginx access_log中开启
$upstream_http_content_length等字段,确认心跳帧有字节流过
配合启用TCP保活与连接复用
Nginx层面的心跳只是应用层保障,底层TCP连接仍可能被防火墙、NAT网关静默回收。需叠加操作系统级保活机制。
- 在Nginx配置中启用
proxy_socket_keepalive on,并设置tcp_keepalive_time 300(5分钟)、tcp_keepalive_interval 15、tcp_keepalive_probes 3 - 确保后端服务(如Spring Boot、Tornado)未覆盖或禁用系统TCP keepalive,默认值(2小时)远长于Nginx心跳窗口,无需调整
- 添加
keepalive_timeout 75s,让Nginx自身对客户端的HTTP连接也保持略长于心跳周期,避免握手阶段就被断开











