Sec-WebSocket-Protocol 是 WebSocket 握手时协商应用层语义的唯一 HTTP 头,仅用于字符串匹配子协议名(如 json-rpc),服务端严格校验客户端所传协议是否在支持列表中并原样回写,不解析、不降级、不自动兼容。

Sec-WebSocket-Protocol 是 WebSocket 握手阶段唯一用于协商应用层语义的 HTTP 头,它不是加密机制、不改变传输格式、也不影响连接建立本身,而纯粹是客户端与服务端之间「约好用什么规则说话」的信号。
WebSocket 子协议本质就是字符串匹配
子协议名称只是一个区分用途的字符串,比如 json-rpc、graphql-ws、json.reliable.webpubsub.azure.v1。服务器收到客户端发来的 Sec-WebSocket-Protocol: json-rpc, xml-rpc 后,只做三件事:检查是否在自己支持列表里、选一个(通常是第一个匹配项)、在响应头里原样回写。没有解析、不校验版本、不自动降级。
FastAPI 中 accept() 的 subprotocol 参数必须严格匹配
如果你在 websocket.accept(subprotocol="json-rpc") 里写了某个值,那客户端传来的 Sec-WebSocket-Protocol 列表中**必须包含完全相同的字符串**,否则握手失败,浏览器控制台会报 WebSocket connection to '...' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 服务端写
await websocket.accept(subprotocol="json-rpc")→ 客户端必须传["json-rpc"]或["json-rpc", "xml-rpc"] - 服务端写
await websocket.accept(subprotocol="json-rpc-v2")→ 客户端传["json-rpc"]不行,大小写/连字符/版本号都算不同协议 - 不传
subprotocol参数(即await websocket.accept())→ 服务端不声明协议,客户端也不能带Sec-WebSocket-Protocol头,否则被拒绝
客户端 new WebSocket() 的第二个参数是协议优先级列表
JavaScript 中 new WebSocket(url, protocols) 的 protocols 是字符串数组,顺序代表客户端偏好。服务端 handleProtocols 或 FastAPI 的 accept() 逻辑通常取第一个匹配项 —— 所以把最稳定、最常用、最兼容的协议放前面。
- 传
["protobuf.reliable.webpubsub.azure.v1", "json.reliable.webpubsub.azure.v1"]→ 服务端若只支持后者,就会选中它并返回Sec-WebSocket-Protocol: json.reliable.webpubsub.azure.v1 - 传空数组
[]或不传 → 不发送Sec-WebSocket-Protocol头,等价于无子协议协商 - 协议名含非法字符(如空格、斜杠、控制符)会导致连接直接被浏览器拦截,错误信息为
Failed to construct 'WebSocket': The subprotocol contains invalid characters
子协议不等于消息格式,但强烈建议保持一致
子协议本身不强制你用 JSON 或 Protobuf 封包,但实际中几乎所有主流子协议(如 graphql-ws、json.reliable.webpubsub.azure.v1)都隐含了消息结构、控制帧语义和重连逻辑。你如果协商了 json-rpc 却发裸字符串,服务端大概率解析失败;协商了 Azure 可靠协议却跳过连接恢复流程,消息就真的会丢。
- 子协议是契约,不是装饰:一旦选择,就要按其规范收发数据、处理错误、响应 ping/pong
- 调试时先用
chrome://net-internals/#websockets查看真实收发的Sec-WebSocket-Protocol值,比猜更可靠 - CDN 或反向代理(如 Nginx)默认可能剥离或改写该头,需显式配置
proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
json.reliable.webpubsub.azure.v1 却没处理 sequenceId 和 resume 帧,可靠性就形同虚设。

















