nginx代理websocket出现400错误的本质是未正确透传upgrade和connection头部,导致后端无法识别协议升级请求而返回错误;需配置proxy_http_version 1.1、proxy_set_header upgrade $http_upgrade、proxy_set_header connection "upgrade"三要素,并确保无if块干扰或头字段被过滤。

WebSocket 代理出现异常响应状态(如 400、403、502、200)时,本质不是 Nginx 主动“返回”这些状态码,而是它未能正确完成协议升级或透传请求,导致后端拒绝处理、提前关闭连接,或 Nginx 自身拦截了握手流程。处理关键在于定位是哪一环中断了 Upgrade 流程,并针对性修复。
识别真实错误来源:先看日志,再查状态码含义
不同状态码指向不同故障层:
-
200:Nginx 把 WebSocket 握手当普通 HTTP 请求转发,后端没收到 Upgrade 头,直接返回 HTML 或默认页面——说明
Upgrade和Connection头根本没透传过去 -
400:常见于请求头损坏(如
$http_upgrade为空、large_client_header_buffers不足、underscores_in_headers丢头),或客户端发了非法 Upgrade 值(如大写Upgrade) -
403:多因后端校验
Origin头失败(尤其跨域场景),或 Nginx 配置了proxy_set_header Origin xxx但值不合法/缺失,也可能是 SSL 端口非标准(如 wss://domain:8443 未在 Nginx 显式声明监听) -
502:上游服务未响应、已崩溃,或 Nginx 在等待 101 响应时超时断开(
proxy_read_timeout过短),也可能是proxy_pass路径拼接错误导致后端 404
确保 Upgrade 请求头完整透传
这是所有异常响应的共性前置条件。Nginx 必须原样把客户端的 Upgrade 意图交给后端:
- 用
proxy_set_header Upgrade $http_upgrade;——$http_upgrade是动态变量,仅当客户端带Upgrade: websocket时才非空,安全可靠 - 用
proxy_set_header Connection "upgrade";—— 注意必须小写upgrade,不能写成Upgrade或keep-alive - 避免在
if块里设置这两个头,Nginx 的if是伪指令,容易跳过执行;统一放在location顶层 - 检查是否有
rewrite或return指令提前终止请求,导致头未被设置
匹配后端行为,补全关键请求头
某些后端框架(如 Spring Boot、Swoole)对请求头敏感,缺一不可:
- 显式传递
Host:proxy_set_header Host $host;,防止后端因 Host 不匹配拒绝请求 - 跨域场景必须传
Origin:proxy_set_header Origin $scheme://$host:$server_port;或按需设为$http_origin(若前端明确发送) - HTTPS 下确保
proxy_set_header X-Forwarded-Proto https;,否则后端可能误判为 HTTP 而拒绝 wss 升级 - 禁用 Nginx 对下划线字段的过滤:
underscores_in_headers on;(若客户端带X-User-ID类字段)
验证与兜底:从客户端到后端逐层确认
不要只依赖浏览器控制台的状态码,要形成闭环验证:
- 用
curl手动模拟握手:curl -i -N -H "Connection: upgrade" -H "Upgrade: websocket" http://your-domain/ws,观察响应是否含101 Switching Protocols - 在 Nginx 中临时加调试日志:
log_format debug '$remote_addr - "$request" $status "$http_upgrade" "$http_connection" "$http_origin"';,确认实际收到的头值 - 检查后端访问日志,确认请求是否抵达、是否触发 WebSocket handler(而非落入 MVC 默认路径)
- 开启后端 warn 级以上日志,搜索
handshake failed、origin mismatch、invalid key等线索











