websocket握手失败主因是代理链路未透传upgrade/connection头,需逐层验证:浏览器network看状态码定位断点,tcpdump抓包确认头是否透传,nginx location块中必须配置proxy_http_version 1.1、proxy_set_header upgrade $http_upgrade、proxy_set_header connection "upgrade"三行且位置正确。

WebSocket 握手失败通常不是后端代码问题,而是代理链路中某个环节没把关键升级头传过去。排查要从客户端发起请求开始,一层层验证是否“升级信号”完整抵达后端。
看浏览器 Network 面板确认失败类型
打开 Chrome DevTools → Network → 过滤 ws 或 websocket,点击失败连接:
- 如果状态显示 (failed) 或 net::ERR_CONNECTION_REFUSED:TCP 层就断了,检查 Nginx 是否监听对应路径、防火墙/安全组是否放行、后端服务是否真实运行并监听在 proxy_pass 指向的地址
- 如果状态是 400 Bad Request 或 426 Upgrade Required:说明请求到了后端,但握手头缺失或格式不对,重点查 Nginx 头转发配置
- 如果状态是 502 Bad Gateway 或 504 Gateway Timeout:Nginx 能连后端,但没收到有效响应,可能是后端未启动 WebSocket 端点,或 Nginx 与后端通信协议版本不匹配(如用了 HTTP/1.0)
抓包验证 Upgrade 和 Connection 头是否透传
在 Nginx 服务器上执行:
tcpdump -i any -A port 80 or port 443 | grep -i -E "(upgrade|connection:.*upgrade|sec-websocket)"同时用浏览器发起 WebSocket 连接。观察输出中:
- 客户端发来的请求里是否有 Upgrade: websocket 和 Connection: Upgrade
- Nginx 转发给后端的请求里这两个头是否还在——若缺失,说明 proxy_set_header 配置没生效或写错位置
检查 Nginx 配置中三处关键设置是否齐全
进入对应 location 块,确认以下三行都在且位置正确(必须在 proxy_pass 之前):
- proxy_http_version 1.1; —— 强制使用 HTTP/1.1,因为 Upgrade 机制仅在 1.1 中定义
- proxy_set_header Upgrade $http_upgrade; —— 动态透传客户端的 Upgrade 头($http_upgrade 是 Nginx 内置变量,值来自请求头 Upgrade)
- proxy_set_header Connection "upgrade"; —— 固定设为 upgrade,不能写 $http_connection,否则普通 HTTP 请求会被误升级
注意:这两行 header 设置必须放在 location 内,不能只写在 server 或 http 块顶层;且不能被其他 proxy_set_header 覆盖。
查 Nginx error.log 定位具体拦截点
执行:tail -f /var/log/nginx/error.log,再触发一次连接。重点关注以下几类日志:
- upstream prematurely closed connection:后端在返回 101 前就关闭了连接,可能是后端服务未启用 WebSocket 支持,或路径配置错误(如 Spring Boot 的 /ws/** 未匹配到)
- client sent invalid request 或 invalid Upgrade header:Nginx 自己拒绝了请求,常见于 SSL 终止时 header 被重写、或启用了某些安全模块(如 mod_security)拦截了非标准头
- 无任何相关日志:说明请求根本没进到这个 location,检查 location 匹配规则(如 /ws/ 和 /ws 区别)、rewrite 重定向是否意外跳走











