Nginx代理WebSocket握手失败报400或403,本质是Upgrade协议升级请求未完整透传至后端:必须配置proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade"三要素,并确保Request Headers中含Upgrade: websocket、Connection: Upgrade、Sec-WebSocket-Key和Origin四字段齐全。

WebSocket在Nginx代理下握手失败,报错如 Error during WebSocket handshake: Unexpected response code: 403 或 400,问题几乎都出在握手阶段——不是连接不通,而是协议升级请求没被正确透传或校验拦截了。排查要聚焦“HTTP Upgrade流程是否完整抵达后端”,而不是查CORS头或缓存。
看浏览器发起的握手请求到底带了什么
打开开发者工具 Network 面板,筛选 ws 或 wss 请求,点开失败的连接,检查 Headers → Request Headers:
- 必须有
Upgrade: websocket - 必须有
Connection: Upgrade - 必须有
Sec-WebSocket-Key(一串Base64值) - 必须有
Origin(例如https://admin.example.com)
如果这四个头缺任何一个,说明前端代码、CDN、或Nginx在上游就过滤/改写了请求——比如某些CDN会删掉 Sec-WebSocket-Key,导致后端无法生成合法响应。
查Nginx有没有正确透传Upgrade协议
Nginx默认不识别WebSocket,必须显式配置才能把Upgrade请求原样转发。检查 location 块中是否有以下三行(缺一不可):
-
proxy_http_version 1.1;(必须是1.1,HTTP/1.0不支持Upgrade) -
proxy_set_header Upgrade $http_upgrade;(把客户端的Upgrade头传下去) -
proxy_set_header Connection "Upgrade";(注意引号,这是固定字符串,不是变量)
常见错误:写成 Connection $http_connection,或漏掉引号导致Nginx尝试解析变量而失败;或者用了 proxy_pass http://backend/ 末尾斜杠,触发了URI重写,意外修改了请求路径和头。
确认后端是否收到并处理了Origin校验
WebSocket跨域不是靠 Access-Control-Allow-Origin 响应头,而是靠后端读取请求中的 Origin 头做白名单判断。403错误基本意味着后端框架(如Spring Boot、Socket.IO、FastAPI)拒绝了该来源。
- 查看后端日志,搜索
Origin:或origin check failed类关键词 - 确认后端配置是否只允许
localhost或测试域名,生产环境的https://yourapp.com是否在白名单里 - 临时关闭Origin校验(仅用于验证),看连接是否成功——如果成功,问题就锁定在后端鉴权逻辑
注意:Nginx 的 add_header Access-Control-Allow-Origin "*" 对WebSocket握手完全无效,别浪费时间加这个。
抓包验证代理链路是否中断
如果以上都对,但还是失败,用 tcpdump 或 Wireshark 在Nginx服务器上抓包:
- 运行
tcpdump -i any port 80 or port 443 -w ws.pcap - 复现一次连接,停止抓包,用Wireshark打开
- 过滤
http.request.method == "GET" and http.upgrade == "websocket" - 看Nginx发出的请求是否含Upgrade头,再看后端返回的响应是否为
HTTP/1.1 101 Switching Protocols
如果Nginx发出去的请求没有Upgrade,说明配置未生效或被其他location覆盖;如果后端返回了101但浏览器仍报错,可能是TLS层问题(如SNI不匹配、证书链不全)或中间设备(防火墙、WAF)主动断连。


















