400错误源于Nginx未完整透传Upgrade和Connection头或协议版本不匹配:必须配置proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade",三行紧贴proxy_pass,避免if块干扰,并检查请求头大小、变量空值、CDN开关及后端响应一致性。

400 错误在 Nginx 代理 WebSocket 场景下,几乎都源于握手请求头丢失或协议版本不匹配——不是后端不支持,而是 Nginx 没把“要升级”的信号完整、稳定地传过去。
确保 Upgrade 和 Connection 头正确透传
Nginx 默认把 Upgrade 和 Connection 当作逐跳头(hop-by-hop),不会转发给后端;再加上可能降级为 HTTP/1.0 转发,后端收不到升级意图,自然返回 400。
-
必须显式启用 HTTP/1.1:
proxy_http_version 1.1;这是前提,HTTP/1.0 不支持 Upgrade 机制 -
Upgrade 头要动态取值:用
proxy_set_header Upgrade $http_upgrade;,不能硬写"websocket",否则客户端未带 Upgrade 头时会出错 -
Connection 值必须小写:写成
"upgrade"(全小写),写成"Upgrade"或"connection"后端通常拒绝 -
三行必须紧挨
proxy_pass:顺序错、中间插其他proxy_set_header都会导致失效,Nginx 不报错但实际不生效
避免 if 块或 location 冲突导致头丢失
偶发性 400 很容易出现在多 location 或含 if 的配置中——if 是伪指令,$http_upgrade 可能为空,相关头就根本没设置。
- 不要在
if块里写proxy_set_header Upgrade或Connection - 把三行关键配置统一放在
location块顶层,紧贴proxy_pass - 如果同时代理普通 API 和 WebSocket,建议拆开路径,比如
location /api/和location /ws/,避免 header 冲突或条件覆盖 - 用
curl -v -H "Upgrade: websocket" -H "Connection: Upgrade" http://your-domain/ws直接测试 Nginx 是否透传了头
排查请求头大小与变量空值问题
看似稳定的配置,可能因 Cookie、Origin、自定义 Header 长度波动而偶发失败——某次刚好超限,Nginx 就静默截断请求头。
- 检查
large_client_header_buffers是否足够:默认4 8k,可临时调大验证,如8 16k - 前端应精简 WebSocket 路径下的 Cookie,例如设
Path=/ws; SameSite=Strict,避免全站 Cookie 泄露 - 开启 debug 日志确认
$http_upgrade实际值:log_format debug '$remote_addr - "$request" $status "$http_upgrade" "$http_connection"'; - 若日志中
$http_upgrade为空,说明客户端根本没发 Upgrade 头(如旧浏览器、测试工具误用 HTTP/1.0)
验证后端响应一致性与 CDN 干扰
400 不一定来自 Nginx,也可能是后端在高负载、超时或异常路径下跳过校验、提前返回空响应。
- 开启后端 error_log 的 warning 或 info 级别,重点看握手阶段是否报错(如 Sec-WebSocket-Key 校验失败)
- 检查 CDN 是否关闭 WebSocket 支持(如 Cloudflare 默认禁用,需手动开启 “WebSockets” 开关)
- 确认 Nginx 未启用
underscores_in_headers on:若客户端带下划线字段(如X-Client-ID),Nginx 会静默丢弃,error_log 设为warn才提示 “invalid header” - 用浏览器开发者工具对比“直连后端”和“经 Nginx 代理”的请求头差异,快速定位丢头环节


















