awen.notes
← 返回文章列表

nginx 反代 WebSocket 的几个坑

2026-11-05 · 约 5 分钟读完

给一个本地服务套 nginx 反代,普通 HTTP 请求一切正常,但页面里的 WebSocket 连接总是连上几秒就断。日志里后端只是看到一条“连接关闭”,什么线索都没有。

这类问题基本都出在同几个配置上,按顺序排查一遍就能定位。

坑一:HTTP/1.1

WebSocket 的握手依赖 HTTP/1.1,而 nginx 反代默认用 HTTP/1.0。少了这一行,握手请求到达后端时协议版本就不对:

proxy_http_version 1.1;

坑二:Upgrade 和 Connection 头

这两个头必须显式透传,否则后端收不到升级请求:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

注意 Connection 这里写死 "upgrade" 是最省心的。网上有写法用 $connection_upgrade 配合 map 让它按请求头动态取值,理论上更严谨,但对单服务反代来说没必要多绕一层。

坑三:读超时

这个最隐蔽。WebSocket 是长连接,如果空闲时间超过 proxy_read_timeout(默认 60 秒),nginx 会主动断开。表现就是“连上了一会儿,然后莫名其妙断了”。

proxy_read_timeout 3600s;
proxy_send_timeout 3600s;

设多长取决于业务有没有心跳。有应用层心跳的话,60 秒其实也够;没有心跳、又希望长连的,就调大。

坑四:upstream 的 keepalive

如果用了 upstream 块,想复用后端连接,还得加上:

upstream backend {
    server 127.0.0.1:8080;
    keepalive 32;
}

并在 location 里补 proxy_set_header Connection "";。这里有个容易踩的点:Connection "" 和前面的 Connection "upgrade" 是冲突的两套做法,前者用于普通请求复用连接,后者用于 WebSocket 升级。如果同一个 location 既要普通请求又要 WebSocket,就得用 map 判断 $http_upgrade 是否为空来决定。

怎么确认是不是 nginx 断的

先看 nginx 的 error log:

tail -f /var/log/nginx/error.log

如果断连时出现 upstream timed out,基本就是超时配置的问题。如果什么日志都没有,断连是应用层主动发的,那就得去查后端。

另外用 curl 手动带上升级头,可以快速验证 nginx 这一层通不通:

curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  https://example.com/ws

能拿到 101 Switching Protocols,说明反代链路没问题,问题在后端。