proxy_read_timeout是websocket断连的直接原因,其默认60秒值会误杀静默合法连接;应设为业务心跳间隔2–3倍(如90–120秒),并同步配置proxy_send_timeout、upstream keepalive及系统tcp参数。

proxy_read_timeout 是 WebSocket 断连的直接推手
Nginx 默认 proxy_read_timeout 为 60 秒,它不区分 HTTP 或 WebSocket —— 只要连续 60 秒没从 Go 后端读到任何数据(包括心跳帧、业务消息),Nginx 就会主动关闭连接。Go 的 net/http 或 gorilla/websocket 服务本身不会因此报错,但客户端会收到 WebSocket is closed before the connection is established 或直接断在 EOF,日志里常见 upstream prematurely closed connection。
这不是 Go 服务的问题,而是 Nginx 单方面“认为连接已失效”后发起了 FIN 包。你改 Go 的 SetReadDeadline 没用,因为断连发生在代理层,Go 还没机会读。
-
proxy_read_timeout必须显式设大,比如proxy_read_timeout 86400;(24 小时) - 该值应 ≥ 前端心跳间隔 + 网络抖动余量(例如前端每 45 秒发一次 ping,这里至少设 60 秒)
- 不要设为 0(Nginx 不支持无限超时,会退化为默认 60 秒)
Upgrade 和 Connection 头没透传,握手就失败
Go 的 WebSocket 库(如 gorilla/websocket)严格校验 Upgrade: websocket 和 Connection: Upgrade 请求头。Nginx 默认不转发这两个头,导致握手请求到达 Go 服务时只剩普通 HTTP 头,服务直接返回 400 或 500,浏览器 Network 面板里看到状态码是 200 或 502,而不是预期的 101 Switching Protocols。
必须在 location 块中加这两行:
-
proxy_set_header Upgrade $http_upgrade;—— 把客户端原始的Upgrade头原样传下去 -
proxy_set_header Connection "upgrade";—— 注意双引号,避免被 Nginx 当成变量解析成空字符串
漏掉任意一行,Go 服务都收不到升级信号,Upgrader.Upgrade() 会直接 panic 或返回错误。
proxy_buffering off 是防止 Go 推送消息被截断的关键
WebSocket 是流式双向通信,Go 服务可能随时向客户端推送消息(比如通知、事件)。如果 Nginx 开启了 proxy_buffering on(默认值),它会把 Go 发来的多个小帧攒在一起,等缓冲区满或超时才转发——这破坏了 WebSocket 帧边界,客户端收到的是拼接乱序的数据,gorilla/websocket 解析失败,抛出 websocket: bad write message type 或静默丢弃。
必须关掉:
-
proxy_buffering off;—— 强制逐帧透传,不缓存 - 顺带检查
proxy_buffer_size和proxy_buffers,它们对 WebSocket 无意义,可不配或保持默认
Go 服务监听地址和 Nginx proxy_pass 要严格匹配协议与路径
Nginx 的 proxy_pass 如果带尾部斜杠(如 proxy_pass http://localhost:8080/;),会剥离 location 路径再转发;不带(如 proxy_pass http://localhost:8080;)则原样转发。Go 服务路由依赖路径(比如 /ws),若 Nginx 错误地重写了路径,Go 收到的 r.URL.Path 就不是预期值,Upgrader.Upgrade() 可能因路径不匹配拒绝握手。
典型配置组合:
- Nginx:
location /ws/ { proxy_pass http://localhost:8080/; } - Go:
http.HandleFunc("/ws", handler)(注意:Nginx 剥离了/ws/,所以 Go 写/ws而非/ws/) - 若用
proxy_pass http://localhost:8080;,Go 就得写/ws/才能匹配
HTTPS 场景下还要确保 Nginx 传了 X-Forwarded-Proto: wss,否则 Go 服务生成的重定向 URL 可能变成 ws://,浏览器直接拒绝连接。











