nginx 不参与 websocket 子协议协商,仅透传 sec-websocket-protocol 请求头与响应头;后端负责匹配、选择并返回一致协议名,nginx 全程不解析、不校验、不改写、不路由子协议。

Nginx 本身不参与、也不处理 WebSocket 子协议(Sec-WebSocket-Protocol)的协商过程。它只负责透传该头部,不做匹配、选择、校验或改写。
子协议协商完全由后端服务决定
客户端在发起 WebSocket 连接时带上 Sec-WebSocket-Protocol: json-rpc, chat.v1,Nginx 会原样转发这个请求头给后端;后端(如 FastAPI、ws、websockets 等)根据自身支持的协议列表进行匹配,并在 101 响应中回写 Sec-WebSocket-Protocol: json-rpc —— Nginx 同样原样透传该响应头回客户端。整个过程 Nginx 不介入逻辑判断。
- 服务端返回的协议名必须与客户端所列之一**完全一致**(区分大小写、连字符、点号),否则浏览器会认为协商失败,
ws.protocol为空 - Nginx 不会过滤、重写、降级或默认 fallback 任何子协议名
- 若后端未返回该响应头,或返回值不在客户端列表中,连接仍可建立,但子协议未生效
Nginx 配置只需确保头部透传
要让子协议协商成功,关键不是 Nginx “支持”它,而是不能阻断它。需在 location 块中显式启用以下两项:
-
proxy_set_header Upgrade $http_upgrade;—— 转发客户端的Upgrade和Sec-WebSocket-Protocol头 -
proxy_set_header Connection "upgrade";—— 保持升级通道畅通
注意:$http_upgrade 是 Nginx 内置变量,自动捕获原始请求中的 Upgrade 头及其关联头(含 Sec-WebSocket-Protocol),无需额外配置。
常见干扰场景与排查要点
子协议协商失败,往往不是因为 Nginx 不支持,而是被意外拦截或遗漏:
- 代理链中存在其他中间件(如 CDN、WAF、网关)过滤了
Sec-WebSocket-Protocol头 - Nginx 配置漏掉
proxy_set_header Upgrade $http_upgrade,导致该头根本未发往后端 - 后端服务未显式调用
accept(subprotocol="xxx")或未实现handleProtocols回调,导致响应头缺失 - 浏览器控制台看到连接成功但
ws.protocol === "",应立即检查 Network → WS → Headers 中响应头是否包含且值匹配
不支持的功能边界
需要明确的是,Nginx 对子协议有明确的能力边界:
- ❌ 不解析子协议语义(比如不知道
graphql-ws要求connection_init帧) - ❌ 不校验协议名合法性(如含空格或斜杠),这部分由浏览器在构造
new WebSocket()时拦截 - ❌ 不支持运行时切换子协议(必须重建连接,Nginx 仅透传新建连接的握手)
- ❌ 不做协议路由(无法根据
Sec-WebSocket-Protocol将请求分发到不同后端)











