400错误根源是WebSocket升级请求头(Upgrade/Connection)未完整透传至后端:需检查Nginx配置中proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade"是否正确设置且未被if/rewrite干扰,同时排查large_client_header_buffers超限、underscores_in_headers丢头、CDN关闭WebSocket支持及后端握手响应一致性。

400 错误在 Nginx 代理 WebSocket 场景下,几乎都指向同一个问题:客户端发起的协议升级请求没完整抵达后端。Nginx 没有把 Upgrade: websocket 和 Connection: Upgrade 这两个关键头透传过去,后端收不到升级意图,只能按普通 HTTP 处理,于是返回 400。排查要从“请求是否发出→是否被 Nginx 拦截或改写→是否到达后端→后端是否响应 101”这条链路逐层验证。
看 Nginx 错误日志,确认丢头或超限线索
打开 Nginx 的 error_log(建议设为 warn 或 info 级别),搜索以下关键词:
-
client sent too large request header或header too large:说明请求头超出large_client_header_buffers限制,常见于 Cookie 过长、带大量自定义 Header 或 A/B 测试参数;可临时调大(如large_client_header_buffers 8 16k)验证 -
invalid header:大概率是启用了underscores_in_headers on,而客户端发了带下划线的字段(如X-Client-ID),Nginx 默认静默丢弃 -
http_upgrade is empty类似提示:说明$http_upgrade变量为空,即客户端根本没发Upgrade头——可能是测试工具误用 HTTP/1.0、浏览器兼容性问题,或前端构造连接时 URL 协议写错(比如用了ws://但服务端只监听wss://)
抓包验证请求头是否真正透传
在 Nginx 服务器上用 tcpdump 或 ngrep 抓取到后端的请求流量,确认实际发给上游的请求里是否包含:
-
Upgrade: websocket(注意大小写,必须全小写websocket) -
Connection: upgrade(同样必须小写upgrade,不是Upgrade或Keep-Alive) -
Sec-WebSocket-Key:24 字符合法 Base64,不能为空或格式错误
如果这些头在抓包中缺失,说明 Nginx 配置未生效或被覆盖;如果存在但后端仍 400,则问题转向后端校验逻辑(如 Origin 白名单、Key 解析失败)。
检查配置是否被 if、rewrite 或多 location 干扰
Nginx 的 if 是伪指令,容易跳过 proxy_set_header 设置;rewrite 或 return 也可能提前终止请求流程,导致头未设置就转发:
- 确保
proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade"直接写在location块顶层,不嵌套在if内 - 避免用
if ($request_method = GET) { ... }这类判断来条件设置 Upgrade 头——它不可靠 - 检查是否有多个
location /ws和location /冲突,导致请求被错误匹配到无 WebSocket 配置的块
确认 CDN 或中间网关是否关闭 WebSocket 支持
如果域名经过 Cloudflare、阿里云 WAF、腾讯云 CDN 等中间层,它们默认会拦截 Upgrade 请求:
- Cloudflare 需在仪表盘开启 “WebSockets” 开关(免费版支持)
- 阿里云 WAF 要在“防护策略”中启用 WebSocket 协议识别
- 部分企业防火墙或代理设备会主动剥离
Connection: upgrade,需联系网络管理员确认
最直接的验证方式:绕过 CDN,用 Nginx 服务器 IP + 端口直连后端,若此时正常,则问题一定出在中间网关。











