DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

应用页面能打开,WebSocket 经 Nginx 返回 400:检查升级请求头

普通页面能访问,WebSocket 经 Nginx 握手返回 400,检查实际代理位置、Upgrade 与 Connection 透传及头配置继承,再按握手结果验收。

应用页面和普通接口都正常,浏览器控制台却出现 Error during WebSocket handshake: Unexpected response code: 400。如果失败请求经过 Nginx,先检查真正命中的代理位置有没有向后端传递 WebSocket 升级头。页面能打开只能证明普通 HTTP 请求可达,不能证明协议升级已经成功。

下面针对 Nginx 通过 HTTP/1.1 向后端建立传统 WebSocket 升级连接,适用于具备 WebSocket 代理能力的 Nginx;该能力从 1.3.13 起提供。400 也可能由认证、Origin、路径或应用协议错误引起,因此要先确认后端实际收到的握手内容。

客户端发了 Upgrade,不代表后端收到

在浏览器 Network 面板找到失败的 WebSocket 请求,核对公开 URL、路径,以及 Upgrade: websocket 和 Connection 中的升级标记。再检查后端请求日志或应用的握手诊断。涉及 Cookie、令牌和查询参数时,先清理敏感信息再分享日志。

Upgrade 和 Connection 是逐跳头字段,Nginx 反向代理不会把它们当作普通头直接转给后端。前端包含这些头,而后端没有收到时,应在对应代理配置中显式设置。

若 Nginx 之前还有 CDN 或负载均衡,也要确认升级头能够到达 Nginx。前一层已经删除的头,不能仅靠把后端地址改名来恢复;先核对每一层是否支持这条 WebSocket 路径。

在实际代理位置设置升级头

下面是配置片段示例。map 位于 http 上下文;后端假设在 127.0.0.1:3000 接受原来的 /websocket 路径:

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    server {
        # 保留站点原有的 listen、server_name 与 TLS 配置
        location = /websocket {
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_set_header Host $host;
        }
    }
}

将内容合入已有上下文,不要在现有 http 中再嵌套一个 http。如果相同变量已经定义,复用或调整已有 map,不要重复声明。示例使用精确匹配,/websocket/ 不会命中它;框架使用 /socket.io/ 等其他入口时,应按实际路由配置。

proxy_pass 在这里没有附加 URI,因此保留请求路径。若后端路径不同,需要另行明确映射,不要在排查头字段时顺手增加斜杠并改变路由。Nginx 与应用在不同容器中时,127.0.0.1 是 Nginx 容器自身,也应换成可达的后端地址。

显式写 HTTP/1.1 能兼容仍使用其他默认代理版本的部署,不能假设所有 Nginx 版本的默认值都相同。map 根据收到的 Upgrade 值生成 Connection;没有升级请求时使用 close,避免给所有普通请求都硬写 upgrade。

配置看似齐全时,检查命中与继承

改动了一个 location,请求却被另一条精确匹配或正则路由处理,头设置仍不会生效。按失败的完整路径检查最终代理位置,并确认修改的是当前进程加载的配置。

proxy_set_header 的继承也要一起检查:当前层只要定义了自己的这类指令,就不会继续整组继承上一层的设置。若新增升级头后需要保留父层设置的 Host、认证相关头或其他业务头,应在当前层明确维持所需设置,不能以为各层会逐项合并。

保存原配置,用实际部署的 Nginx 和配置路径执行 nginx -t,成功后再重载。失败时恢复本次变更,避免留下部分头配置。第三方托管环境无法修改代理时,应向有权限的管理员确认 WebSocket 支持,不能仅在前端隐藏报错就认定已修复。

以握手和消息交换验收

在这里的传统 HTTP/1.1 场景中,升级成功应返回 101 Switching Protocols,随后能够交换应用所需消息。普通页面 200、回退到轮询或控制台不再显示同一个错误,都不能单独证明 WebSocket 已恢复。

后端已经收到正确升级头却仍返回 400 时,继续查看具体握手原因:路径、Origin、认证、WebSocket 子协议及框架客户端版本需要分别核对。返回 401 或 403 则优先检查相应授权规则,不能通过关闭校验解决。

如果连接能建立,只在空闲后断开,那是后续连接维持问题。Nginx 默认在后端连续 60 秒没有发送数据时关闭这类代理连接,应结合心跳和 proxy_read_timeout 检查,不要把建立连接前的 400 与空闲超时混在一起。