Go服务在Nginx反向代理后长连接断开,主因是Nginx默认proxy_read_timeout=60秒主动关闭空闲连接,且proxy_http_version 1.1、Upgrade/Connection头缺失导致WebSocket握手失败,TCP keepalive未与系统参数协同。
go服务在nginx反向代理后出现长连接断开,大概率不是go代码问题,而是nginx对空闲连接的默认超时策略与tcp层保活机制未对齐导致的。
proxy_read_timeout 是 WebSocket 和 HTTP 长轮询断连的头号原因
这个参数控制 Nginx 从 upstream(你的 Go 服务)读取响应的等待时间。默认值是 60 秒,一旦连接建立后连续 60 秒没收到任何数据(比如心跳、消息、甚至 TCP ACK),Nginx 就会主动关闭 socket,返回 502 Bad Gateway 或触发客户端 Connection reset by peer。
- WebSocket 场景下,必须设为远大于预期空闲时间,常见值是
3600(1 小时)或86400(24 小时) - HTTP 长轮询(如 SSE)同理,需匹配后端最长可能挂起响应的时间
- 注意:它只影响“读”,不影响“写”;
proxy_send_timeout控制发送超时,一般保持默认即可 - 该指令必须放在
location块内,且优先级高于 server 级配置
proxy_http_version 和 Connection 头缺失导致握手失败或连接被复用中断
Go 的 WebSocket 库(如 gorilla/websocket)要求升级请求必须带 Upgrade: websocket 和 Connection: upgrade。Nginx 默认使用 HTTP/1.0 转发,会丢弃这些关键 header,并可能复用连接池中的旧连接,导致后续请求被错误路由或直接拒绝。
- 必须显式设置
proxy_http_version 1.1 - 必须清除
Connection头:用proxy_set_header Connection ''(空字符串),否则 Nginx 会继承 client 请求里的Connection: keep-alive,干扰 upgrade 流程 - 同时补全
Upgrade头:proxy_set_header Upgrade $http_upgrade - 这三行必须成套出现,缺一不可;漏掉
Connection ''是最常被忽略的点
proxy_socket_keepalive 不是“心跳开关”,而是配合系统 TCP 参数的保活底座
proxy_socket_keepalive 开启的是内核级 TCP keepalive 探测,但它不会主动发应用层心跳,也不改变 Nginx 自身的超时逻辑。它的作用是防止中间设备(防火墙/NAT)因空闲超时静默切断连接——但前提是系统级参数已调优。
- 仅对
proxy_pass连接生效,不作用于 client → Nginx 连接 - 推荐开启:
proxy_socket_keepalive on(Linux ≥ 3.9 可加参数,如proxy_socket_keepalive on 30s 10s 6) - 必须同步调整系统参数:
net.ipv4.tcp_keepalive_time=300(5 分钟)、tcp_keepalive_intvl=30、tcp_keepalive_probes=3 - 若上游是 Kubernetes 中的 Pod,还需确认 CNI 插件是否透传 keepalive 包(部分桥接模式会截断)
Go 服务端未正确处理 Ping/Pong 导致连接被单方面关闭
Nginx 不会转发 WebSocket ping/pong 帧,但 Go 的 gorilla/websocket 默认启用 CheckOrigin 和 SetPingHandler,若客户端发 ping 后服务端无响应,库可能主动关连接。这不是 Nginx 的问题,但现象高度相似。
- 务必在 Go 服务中注册 ping handler:
conn.SetPingHandler(func(appData string) error { return conn.WriteMessage(websocket.PongMessage, nil) }) - 禁用 origin 检查(开发期)或显式放行 Nginx 的 Host:
func(r *http.Request) bool { return true }(生产环境应严格校验) - 检查 Go 日志是否出现
websocket: close sent或use of closed network connection,这类错误说明断开发生在 Go 层 - 可临时用
wscat -c ws://your-nginx/sock直连测试,排除 Nginx 干扰
真正棘手的不是某个参数设错,而是多个层级的超时和保活机制彼此错位:Nginx 的 proxy_read_timeout、内核的 tcp_keepalive_time、Go 库的 ping 超时、中间设备的 idle timeout —— 四者只要有一个比其他更短,连接就可能在无声中消失。调试时建议先统一设为 300 秒,再逐个收紧验证。


















