Nginx 反代 WebSocket 出现 400 或 502 怎么办?Upgrade 头、超时与长连接排查

2026-09-20

直接答案:WebSocket 经 Nginx 反代出现 400 时,先检查是否使用 HTTP/1.1 并显式传递 Upgrade 和合适的 Connection 头;出现 502 时,重点检查上游地址、端口、协议、监听和握手响应。握手成功应看到 101,但 101 只证明升级完成,还要测试双向消息、空闲保持、代理 reload 和断线重连。不要把普通页面 200 当成 WebSocket 可用,也不要简单把所有超时改成一天。

本文面向浏览器控制台显示 WebSocket handshake 400/502、连接建立后约固定时间断开,或应用在 CDN/Nginx 后才失败的场景。Socket.IO 等库可能先用 HTTP 轮询再升级,路径和参数要按实际客户端协议检查。

本文按 2026 年 9 月可查的官方文档核对。示例命令以只读取证为主;凡涉及删除、覆盖、重启、权限、网络或数据库参数的动作,都应先确认操作对象、备份、维护窗口和带外恢复入口。不同发行版与软件版本的路径可能不同,必须先查询实际配置再改。

先用证据判断故障在哪一层

现象或检查点 它能说明什么 直接证据 不要误判为
握手返回 400 升级头、路径、Origin 或应用协议不满足 请求头、Nginx与应用日志 上游一定宕机
握手返回 502 Nginx 无法获得有效上游响应 error log、上游监听与直连 客户端浏览器版本
返回 200 而不是 101 请求被当普通 HTTP 或未升级 网络瀑布与响应头 连接已成功
固定 60 秒左右断开 空闲超时或无 ping/pong 断开时长分布与代理配置 随机网络抖动
直连上游正常、域名失败 代理/CDN路径、TLS或规则问题 逐层握手对照 应用业务逻辑

这张表的用法不是“看到一个关键词就改一个参数”,而是把客户端、DNS或边缘、Web 代理、应用、数据层依次分开。至少保留故障时间、完整 URL、状态码、关键响应头、同一时刻的服务日志和一份正常样本。只有时间能够对齐,才能判断两个现象是否属于同一次故障。

六步排查与修复顺序

1. 固定真实 WebSocket URL

记录 ws/wss、主机、路径、查询参数、子协议、Origin 和认证方式。不要只说“聊天连不上”;同站点可能有多个 WebSocket 端点,普通 API 路径成功不能代表目标端点。

2. 确认上游独立可用

从 Nginx 主机直连应用监听地址,核对进程、端口、协议和路径。若上游使用 TLS,proxy_pass 协议、SNI 和证书要匹配;若容器内监听 127.0.0.1,宿主代理可能无法访问。

3. 按官方方式传递升级头

代理使用 HTTP/1.1,转发 $http_upgrade,并通过 map 为有升级请求设置 Connection: upgrade、普通请求设置 close。不要无条件相信客户端任意 hop-by-hop 头,也不要把配置放进未命中的 location。

4. 对齐路径、Host 与 Origin

检查 location 和 proxy_pass 末尾斜杠是否重写路径,上游是否按 Host 或 Origin 做允许列表。400 可能是应用主动拒绝而非 Nginx 语法错;用应用日志确认拒绝原因。

5. 设计空闲与保活预算

Nginx 默认在上游一段时间无数据时可能关闭连接。根据业务设置 proxy_read_timeout,应用使用 ping/pong 检测存活;CDN 和负载均衡也有各自上限,不能只改一层。

6. 覆盖重连与发布验收

测试握手 101、双向消息、空闲超过阈值、网络短断、令牌过期、Nginx reload 和应用滚动发布。客户端应指数退避重连并避免瞬间风暴;服务端要能清理失效连接和恢复订阅。

可复制的只读取证命令

以下命令中的域名、服务名、路径和时间范围要替换为自己的真实值。先执行查询命令并保存原始输出;不要把“命令能运行”误当成“业务已恢复”。

text ss -lntp | grep ':3000' curl -i --http1.1 -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: SGVsbG9Xb3JsZDEyMzQ1Ng==' https://www.your-domain.tld/ws nginx -T 2>&1 | grep -nE 'proxy_http_version|Upgrade|Connection|proxy_read_timeout|location' curl -sv http://127.0.0.1:3000/health -o /dev/null journalctl -u nginx --since '20 minutes ago' --no-pager

  • ss -lntp | grep ':3000':确认示例上游端口实际监听;替换为真实端口
  • curl -i --http1.1 -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: SGVsbG9Xb3JsZDEyMzQ1Ng==' https://www.your-domain.tld/ws:观察是否进入升级流程;完整协议测试应使用专业客户端
  • nginx -T 2>&1 | grep -nE 'proxy_http_version|Upgrade|Connection|proxy_read_timeout|location':查实际加载的WebSocket相关配置
  • curl -sv http://127.0.0.1:3000/health -o /dev/null:从代理主机验证上游健康路径,不等同WebSocket握手
  • journalctl -u nginx --since '20 minutes ago' --no-pager:读取握手失败窗口内的connect、reset、timeout信息

如命令输出包含公网 IP、用户名、Cookie、令牌、数据库连接串或客户数据,分享工单前先脱敏。对比时保留时间戳、时区和命令本身,否则后来无法复现同一观察条件。

五个最容易扩大故障的误区

  • 只添加 Upgrade 不设置 HTTP/1.1:上游可能仍无法完成协议升级。
  • 普通首页 200 就宣布修复:WebSocket 要看到 101 并验证消息和长连接。
  • 把 proxy_read_timeout 设极大:会长期保留僵尸连接,仍不能解决缺少心跳和上游故障。
  • 忽略 proxy_pass 路径重写:末尾斜杠差异可能让上游收到错误路径并返回 400/404。
  • 客户端无限立即重连:故障时会形成重连风暴,加重代理和应用负载。

变更、回滚与数据边界

保存 Nginx 旧配置并先运行 nginx -t。若新增 map/location 影响普通 HTTP、认证或其他站点,恢复上一配置并平滑 reload。调整长连接超时前记录当前并发与内存;如连接数或资源异常增长,先恢复旧阈值并启用受控重连。消息类业务回滚时还要核对重复投递与订阅状态,不能只看连接重新变绿。

如果只能先恢复服务、还不能确认根因,应把措施标记为“临时缓解”,例如限流、摘除单节点、切回上一配置或临时绕过一层代理。恢复后仍要在受控环境复现原因,并验证同类故障不会在下一次重启、发布或高峰重新出现。

统一验收清单

  • [ ] 目标 ws/wss URL 与路径已明确
  • [ ] 上游监听和直连健康
  • [ ] 握手返回 101 而非 200/400/502
  • [ ] Upgrade 与 Connection 头按条件转发
  • [ ] Host、Origin 与认证符合应用规则
  • [ ] 空闲超过预期窗口仍可用或按设计断开
  • [ ] Nginx reload 后客户端能受控重连
  • [ ] 连接数、内存与错误率无异常增长

验收必须同时覆盖机器内部和真实外部路径。首页返回 200 不能证明上传、长连接、登录、数据库写入或移动端都正常;同样,控制台显示“运行中”也不能证明应用已能处理请求。至少选一个正常请求、一个边界请求和一个失败请求,记录预期与实际结果。

相关内容与隔离测试入口

建议先阅读Nginx反向代理、502与504排查、云服务器端口不通,把本问题放回完整请求链路。若生产环境不适合直接试验,可查看美国弹性云服务器的当前规格与库存,搭建与生产隔离的复现环境;测试机不能替代生产备份,价格、库存和可选配置以实时产品页为准。

常见问题

为什么浏览器报 400,Nginx 配置看起来没错?

可能是应用拒绝路径、Origin、子协议或认证。查看应用日志和真实握手请求,不能只看 Nginx。

WebSocket 一定要设置 Connection upgrade 吗?

这些 hop-by-hop 头不会自动传给上游,Nginx 官方文档给出了显式配置方式。

连接每分钟断一次怎么查?

统计断开时间是否集中固定阈值,逐层核对 CDN、负载均衡、Nginx 和应用空闲超时以及 ping/pong。

Cloudflare 支持 WebSocket 吗?

支持不等于所有路径和套餐行为都相同。仍要核对端口、WAF、超时、日志和源站握手。

502 是不是 Nginx 没加 Upgrade?

不一定。502 更常说明上游连接、协议或响应失败;缺升级头也可能由应用返回 400。以错误日志为准。

官方参考资料

最近更新