Nginx代理Go服务WebSocket频繁断开,主因是未正确透传HTTP/1.1升级语义(缺proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade"),proxy_read_timeout默认60秒过短,且TCP keepalive未协同系统参数启用。

Go服务在Nginx反向代理后出现长连接频繁断开,大概率不是Go代码问题,而是Nginx未正确透传HTTP/1.1升级语义、超时设置过短,或TCP层保活未协同生效。
proxy_http_version 1.1 和 upgrade 头缺失导致握手失败
Go的WebSocket实现(如 gorilla/websocket)严格校验HTTP Upgrade流程。Nginx默认不转发 Upgrade 和 Connection 头,会导致后端拒绝升级,返回400/200而非101,客户端看到“connection closed before established”或直接EOF。
- 必须显式设置
proxy_http_version 1.1:HTTP/1.0不支持Upgrade机制 - 必须透传升级头:
proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade"(注意引号,避免被Nginx当作指令解析) - 若后端监听的是HTTPS(wss),确保Nginx终止TLS,且未在中间设备(如云LB)二次终止——否则upgrade头可能被剥离
proxy_read_timeout 默认60秒引发空闲断连
WebSocket连接建立后无常规HTTP响应流,proxy_read_timeout会从最后一次收到数据起计时。60秒无心跳即触发Nginx主动关闭连接,表现为客户端突然断开、日志无错误。
- 建议设为至少300秒(5分钟):
proxy_read_timeout 300;若业务有明确心跳间隔(如30s ping/pong),该值应 > 2×心跳周期 -
proxy_send_timeout同样需同步调大,防止Nginx在发送ping帧时因等待ACK超时而中断 - 该超时仅作用于upstream连接,不影响client→Nginx连接(由
keepalive_timeout控制)
proxy_socket_keepalive 与系统TCP参数未协同
即使应用层心跳正常,中间网络设备(防火墙/NAT网关)仍可能静默切断空闲TCP连接,表现为偶发502/504或 Connection reset by peer。此时仅调大Nginx超时无效,必须启用TCP keepalive探测。
- 开启指令:
proxy_socket_keepalive on(仅对Linux ≥3.9 / FreeBSD ≥10生效完整参数) - 必须配合系统级配置:
net.ipv4.tcp_keepalive_time=180(建议180–300秒),否则内核默认7200秒才发第一个探针,远超设备空闲阈值 - 注意:
proxy_socket_keepalive不影响client→Nginx连接,也不替代应用层心跳;它只在upstream连接空闲时由内核自动探测 - 需同时启用upstream keepalive池(
keepalive 32),否则每次请求新建连接,keepalive无意义
Go服务端未处理Nginx连接复用带来的并发压力
Nginx启用 keepalive 后,单个upstream连接会被多个HTTP请求复用。若Go服务未正确管理长连接生命周期(如未设置读写deadline、未限制并发goroutine数),可能因资源耗尽导致连接异常关闭。
- 在
http.Server中设置ReadTimeout、WriteTimeout和IdleTimeout,避免单连接长期阻塞 - WebSocket handler中务必使用
conn.SetReadDeadline和conn.SetWriteDeadline绑定心跳周期 - 检查Go服务是否启用了HTTP/2——Nginx目前(截至2026年)对HTTP/2 upstream的keepalive支持有限,易引发连接复位
真正棘手的点在于:断连现象常是多层超时叠加的结果——Nginx的proxy_read_timeout、中间设备的空闲超时、Go服务自身的idle timeout,三者只要有一个先触发,就会表现为连接中断。排查时必须逐层确认,不能只盯着Nginx配置改。


















