nginx 反代 WebSocket 的几个坑
给一个本地服务套 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,说明反代链路没问题,问题在后端。