Nginx 反向代理 WebSocket 断连排查

我的 WebSocket 服务挂在 Nginx 后面,连上之后大概一分钟就会自动断开,直连后端没问题。这是什么原因?

“直连正常、过 Nginx 约 60 秒断开”几乎可以确定是 Nginx 的 proxy_read_timeout。它的默认值正好是 60 秒:如果这段时间内上游没有发来任何数据,Nginx 就会关闭连接。

WebSocket 连接空闲时不传数据,所以会撞上这个超时。

那我应该怎么配?

需要做两件事:一是正确转发 Upgrade 握手,二是调大读写超时。一份可用的配置如下:

nginx
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 443 ssl;
  server_name chat.example.com;

  location /ws/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 空闲超过这个时间才会断开;配合应用层心跳使用
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
  }
}

几点说明:

  • proxy_http_version 1.1 必须写,默认的 1.0 不支持 Upgrade。
  • map 让普通 HTTP 请求也能走同一个 location,不会被强行加上 Connection: upgrade。
  • 超时调大只是兜底,更稳的做法是客户端每 30 秒发一次 ping。

官方文档里关于这几个指令的说明在这里:https://nginx.org/en/docs/http/ngx_http_proxy_module.html?utm_source=pairwise-chat&utm_medium=assistant-answer&utm_campaign=websocket-timeout-troubleshooting&ref=conversation-8f3a2c71-4d9e-4b1a-9c55-e2f7a01b6d38#proxy_read_timeout

前面还有一层云厂商的负载均衡,会不会也有超时?

会。负载均衡通常有自己的“空闲超时”,常见默认值是 60 秒或 350 秒。整条链路的实际超时取最短的那一段,所以 LB、Nginx、应用三层都要检查。

最省心的办法仍然是心跳:只要间隔小于链路上最短的超时,连接就不会因为空闲被断。

心跳间隔设多少合适?

取链路最短超时的一半左右。比如最短是 60 秒,就设 25–30 秒。太短会白白增加流量和服务器负担,移动端还会更耗电。

改完之后怎么验证?
  1. 执行 nginx -t 检查语法,再 nginx -s reload。
  2. 先关掉客户端心跳,用 websocat wss://chat.example.com/ws/ 连上后保持空闲,看是否还在 60 秒时断开。
  3. 确认不断之后再打开心跳,观察几小时的连接时长。
好的,已经不断了,谢谢!

不客气。后面如果遇到别的问题,比如部署后连接数突然变多,可以把 Nginx 的 error log 发给我一起看。

回复由 AI 生成,可能有误,请核对重要信息。