websocket 在 nginx 下报 502 的根本原因是 nginx 默认不支持 http 协议升级,需显式配置 proxy_http_version 1.1、透传 upgrade 和 connection 头、禁用缓冲与超时限制,并确保路径精确匹配及网络可达性。
websocket 在 nginx 反向代理下报 502,基本不是 websocket 协议本身的问题,而是 nginx 没把连接“当回事”——它默认按普通 http 处理,中途断开或拒绝升级,导致上游服务根本没收到 upgrade 请求。
为什么 proxy_pass 默认会杀掉 WebSocket 连接
Nginx 默认不透传 Connection 和 Upgrade 头,也不启用 HTTP/1.1 长连接,结果就是:浏览器发了 Upgrade: websocket,Nginx 把它当普通请求处理,读完响应就关连接,上游服务压根没机会握手。
常见错误日志里会出现:upstream prematurely closed connection while reading response header from upstream 或 client sent invalid request while reading client request line(尤其在带鉴权或路径重写时)。
- 必须显式设置
proxy_http_version 1.1 - 必须清除
Connection头(否则 Nginx 会按 HTTP/1.0 处理):proxy_set_header Connection "" - 必须透传
Upgrade头:proxy_set_header Upgrade $http_upgrade -
$http_upgrade是 Nginx 内置变量,只在请求含Upgrade头时非空;空值时该头不会被发送,安全
location 匹配必须精确,避免路径截断或重写错乱
WebSocket 路径对大小写、斜杠、前缀极其敏感。比如前端连 wss://api.example.com/ws,而 Nginx 配置了 location /ws { proxy_pass http://backend; },但后端实际监听 /ws —— 看似没问题,实则可能因 proxy_pass 末尾斜杠行为导致路径拼接错误。
最稳妥写法是显式补全路径,并关闭自动重写:
location /ws {
proxy_pass http://backend/ws; # 注意末尾 /ws,与后端监听路径严格一致
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
- 若
proxy_pass末尾有/(如http://backend/),Nginx 会删掉匹配的/ws再拼接,容易出错 - 若后端用相对路径处理 upgrade(如 Express 的
app.ws('/ws', ...)),必须保证进来的path是/ws,不能变成/或/ws/ - 避免用正则
location ~ ^/ws,除非你明确控制了$1捕获和重写逻辑
超时配置必须调大,且禁用缓冲
WebSocket 是长连接,但 Nginx 默认的 proxy_read_timeout 是 60 秒,一到时间就主动断开,表现就是连接闪断、502 或 1006 错误。
- 设为 0 表示“永不超时”(仅适用于确定可控的长连接场景):
proxy_read_timeout 0; - 必须禁用响应缓冲,否则 Nginx 会攒着数据不转发:
proxy_buffering off; - 可选加
proxy_cache off;,避免缓存 upgrade 请求(虽然通常不会缓存,但显式关更稳) - 如果后端是 Node.js(如 ws 库)或 Java(如 Spring WebFlux),还要确认其自身心跳/超时设置 ≥ Nginx 值,否则上游先断,Nginx 日志里会报
connection reset by peer
Docker 或宿主机网络环境下容易忽略的点
本地开发常把后端跑在宿主机,Nginx 跑在 Docker 容器里,这时 proxy_pass http://localhost:8080 会连容器自己的 127.0.0.1,而不是宿主机——必然 502。
- Docker 场景下,改用
host.docker.internal(Docker for Mac/Win)或宿主机真实 IP(Linux 下需--add-host=host.docker.internal:host-gateway) - Kubernetes 场景下,确保 Service 和 Endpoint 正常,
kubectl get endpoints your-service必须有 READY 地址 - SELinux 启用时(如 CentOS),可能拦截容器访问宿主机端口,临时验证可运行:
setsebool -P container_connect_host 1 - 别忘了
nginx -t验证配置,reload 前漏这步,502 可能来自语法错误而非逻辑问题
真正麻烦的从来不是配几行参数,而是某处少了个 ""、多了一个 /、或者以为 localhost 在哪都一样。











