Nginx代理WebSocket必须避开HTTP/2,因HTTP/2协议废弃Upgrade机制,导致握手失败;应通过域名分离(如ws.example.com不启用http2)、路径分流或端口隔离实现物理/逻辑隔离,并确保proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade"三者齐备且位置正确。

HTTP/2 本身不支持 WebSocket 的 Upgrade 机制,Nginx 在启用 http2 的 server 块中无法正确处理 WebSocket 握手请求——这不是配置遗漏,而是协议层面的硬性限制。排查核心是确认 WebSocket 流量是否被错误地卷入 HTTP/2 连接通道。
确认是否落入 HTTP/2 server 块
WebSocket 请求必须避开 HTTP/2 上下文。检查 Nginx 配置中匹配 WebSocket 路径(如 /ws/ 或 /websocket/)的 location 是否位于启用了 http2 的 server 块内:
- 若该
server块包含listen 443 ssl http2;,则整个块默认走 HTTP/2,Upgrade 头会被 ALPN 协商静默丢弃; - 即使写了
proxy_http_version 1.1,也无法在已建立的 h2 连接上触发 HTTP/1.1 升级流程; - 查看 access_log:若 WebSocket 请求完全无日志,大概率是浏览器因 SNI 或路径匹配问题根本没发到这个 server 块。
验证 Upgrade 和 Connection 头是否透传
即使路径匹配正确,HTTP/2 环境下仍可能因头处理逻辑差异导致关键字段丢失:
- 用
curl -i -H "Upgrade: websocket" -H "Connection: Upgrade" http://your-domain/ws/模拟握手,观察响应是否含101 Switching Protocols; - 检查 Nginx 配置中是否使用了
proxy_set_header Upgrade $http_upgrade;(变量方式),而非固定字符串; -
proxy_set_header Connection "upgrade";必须带双引号,且不能写成$http_connection(该变量不可靠); - 避免在 HTTP/2 server 中使用
map动态设置Connection,部分版本存在兼容问题。
隔离 WebSocket 流量的实操方案
不强行在 HTTP/2 下“降级”,而是物理或逻辑隔离:
- 域名分离:为 WebSocket 单独配置
ws.example.com,其server块仅写listen 443 ssl;,不加http2; - 端口分离:WebSocket 服务监听独立端口(如 8081),Nginx 用非 443 端口反向代理,彻底绕开 HTTPS/HTTP/2 栈;
- 路径分流慎用:若必须共用域名,可用
map $http_upgrade $ws_protocol+if ($ws_protocol = "websocket") { ... },但需确保该逻辑在非 http2 server 内执行。
检查客户端连接地址是否指向正确入口
常见隐形故障点:
- 前端代码中 WebSocket 地址写的是
wss://localhost/xxx,而实际 Nginx 代理运行在虚拟机或容器中,localhost指向浏览器所在机器,请求根本未到达 Nginx; - HTTPS 页面中混用
ws://(被浏览器主动拦截),或 WSS 地址证书域名与 Nginxserver_name不一致,导致 TLS 握手失败、升级请求未发出; - Docker 环境下未正确映射端口(如宿主机 443 → 容器 80),或容器网络策略阻止了 WebSocket upgrade 流量。


















