nginx 代理 websocket 子协议的关键是透传 sec-websocket-protocol 头,需配置 proxy_set_header upgrade $http_upgrade; 和 proxy_set_header connection "upgrade";,子协议协商完全由后端决定。

要让 Nginx 正确代理带子协议(Sec-WebSocket-Protocol)协商的 WebSocket 服务,核心不是“让 Nginx 支持子协议”,而是确保它不干扰、不丢弃、不改写这个头部——因为 Nginx 本身不参与协商,只做透传。
必须启用的头部透传配置
在对应 location 块中,以下两项缺一不可:
-
proxy_set_header Upgrade $http_upgrade;:Nginx 内置变量$http_upgrade会自动捕获客户端原始请求中的Upgrade头及其关联头(包括Sec-WebSocket-Protocol),原样转发给后端 -
proxy_set_header Connection "upgrade";:保持升级通道畅通,否则握手无法完成
子协议协商完全由后端决定
Nginx 不校验、不匹配、不 fallback 子协议名。能否成功协商,取决于后端是否:
- 收到客户端发来的
Sec-WebSocket-Protocol: json-rpc, chat.v1等值 - 从中选出一个自身支持且**完全一致**(大小写、连字符、点号均敏感)的协议名
- 在 101 响应中准确回写
Sec-WebSocket-Protocol: json-rpc
若后端未返回该响应头,或返回值不在客户端列表中,浏览器会设 ws.protocol = "",但连接仍可建立——只是子协议未生效。
常见失败原因与检查点
子协议协商失败,90% 不是 Nginx 不支持,而是被意外阻断:
- 代理链中存在 CDN、WAF 或网关,主动过滤了
Sec-WebSocket-Protocol头 - Nginx 配置遗漏
proxy_set_header Upgrade $http_upgrade;,导致该头根本未发往后端 - 后端未显式调用
accept(subprotocol="xxx")(如 FastAPI)或未实现handleProtocols回调 - 浏览器 Network 面板 → WS → Headers 中查看响应头,确认是否存在且值严格匹配客户端所列之一
不需要额外配置的误区
这些操作对子协议协商没有帮助,反而可能引入问题:
- 不用手动写
proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;——$http_upgrade已自动包含它 - 不要尝试用
map或if对子协议做路由或重写 —— Nginx 不解析该字段,也无意义 - 无需设置
proxy_pass_request_headers off或自定义 header 过滤逻辑 —— 这会直接破坏协商











