浏览器原生 WebSocket 不支持自定义请求头(如 Authorization),因协议限制仅允许 Sec-WebSocket-Key、Origin 等标准字段;可使用 Sec-WebSocket-Protocol 传 Token 或 URL 参数方式,并确保服务端在握手前完成鉴权。

浏览器原生 WebSocket 构造函数不支持设置自定义请求头(如 Authorization),这是硬性限制,不是配置问题,强行绕过会导致连接失败或被忽略。
为什么 new WebSocket() 不能传 headers
WebSocket 握手本质是一次 HTTP 请求,但浏览器出于安全和协议一致性考虑,只允许客户端发送 RFC 定义的固定头字段(如 Sec-WebSocket-Key、Origin),所有其他自定义头(包括 Authorization、X-Token)都会被浏览器静默丢弃。
常见错误现象:
- 前端代码写成 new WebSocket(url, { headers: { Authorization: 'Bearer xxx' } }) → 控制台报错或连接直接失败
- 抓包发现请求中根本没有该 header → 说明浏览器根本没发出去
- 后端始终收不到 request.getHeaders().getFirst("Authorization") → 不是后端漏读,是前端压根没发
- 这个限制适用于所有主流浏览器(Chrome/Firefox/Safari/Edge),与 WebSocket 协议版本无关
-
socket.io、ws(Node.js 客户端)等库能“设 header”,是因为它们不是浏览器原生 API,而是自己封装的 HTTP 请求 + 升级逻辑 - 不要尝试用
fetch+Response.upgrade()模拟——该 API 已被多数浏览器废弃或限制使用
Sec-WebSocket-Protocol 是唯一合法的“伪 header”传参方式
虽然不能加任意 header,但 Sec-WebSocket-Protocol 是 WebSocket 协议明确允许客户端声明的字段,浏览器会原样带上,服务端可安全读取。它本质是子协议协商机制,但常被借用来传递 Token 或用户标识。
前端写法(Vue/React/原生 JS 均适用):const socket = new WebSocket("wss://api.example.com/ws", ["auth-v1:eyJhbGciOi"])
- 第二个参数必须是
string[],哪怕只传一个值也要写成数组 - 值只能含 ASCII 字母、数字、短横线(
-)、点(.)、下划线(_),不能有空格、@、/、中文等;JWT 中的+、/、=需 Base64URL 安全编码(推荐用jwt-simple或后端对应解码逻辑) - 后端读取方式(Spring Boot 示例):
request.getHeader("Sec-WebSocket-Protocol")→ 返回类似"auth-v1:eyJhbGciOi"的字符串,需按分隔符解析
URL 参数传 Token 是最兼容、最稳妥的 fallback 方案
当子协议方式受限(比如 Token 太长、含非法字符、或后端框架不便于解析 Sec-WebSocket-Protocol)时,URL 查询参数仍是首选。它不依赖任何浏览器新特性,100% 兼容,且服务端解析极其简单。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
前端示例:
const token = localStorage.getItem("auth_token");<br>
const ws = new WebSocket(`wss://api.example.com/ws?token=${encodeURIComponent(token)}`);
- 务必用
encodeURIComponent()编码 token,否则特殊字符(如+、=)会被截断或解析错误 - 服务端从
request.getRequestURL()或request.getParameter("token")获取,无需额外中间件 - 安全性注意:Token 会出现在浏览器地址栏、服务端 access log、代理日志中,必须强制 HTTPS,且 Token 有效期要短(建议 ≤ 5 分钟)
- 不要把敏感字段(如用户 ID、手机号)明文拼进 URL,应统一走签名 Token 校验
反向代理注入 header(Nginx / Cloudflare)是生产环境进阶选择
如果你控制着部署链路(比如 Nginx 在前端和 WebSocket 服务之间),可以在代理层把 URL 参数或子协议值,转换为标准 Authorization 头转发给后端,这样后端就能用熟悉的鉴权逻辑处理。
Nginx 配置片段示例:
location /ws {<br>
proxy_pass https://backend;<br>
proxy_http_version 1.1;<br>
proxy_set_header Upgrade $http_upgrade;<br>
proxy_set_header Connection "upgrade";<br>
proxy_set_header Authorization "Bearer $arg_token"; # 从 URL ?token=xxx 提取<br>
# 或 proxy_set_header Authorization "Bearer $http_sec_websocket_protocol"; # 从子协议提取<br>
}
- 这种方式让后端完全 unaware 前端传参方式,复用现有 JWT 解析器即可
- 要求你有代理层控制权,不适合纯前端托管(如 Vercel、Netlify)场景
- 注意 Nginx 默认不透传带下划线的 header(如
Sec-WebSocket-Protocol),需显式开启underscores_in_headers on;
真正容易被忽略的是:无论用哪种方式,**鉴权必须在握手完成前完成**。如果等到 onOpen 之后再校验、再 close 连接,那个未授权的 socket 已经占着资源、可能已被恶意利用。所有方案的核心落点,都是让服务端在 beforeHandshake(Spring)、verifyClient(ws 库)或 Nginx rewrite 阶段就做出拒绝决定。










