WebSocket握手阶段可用Cookie验证身份,但必须在upgrader.Upgrade()前完成校验;浏览器同源时自动发送Cookie,需满足Secure、SameSite(非Strict)、Domain/Path匹配三项硬性条件,跨域时还需前后端协同配置Credentials与CORS头。

WebSocket 握手阶段能用 Cookie 验证身份,但必须在 Upgrade 请求完成前完成校验 —— 升级之后,Cookie 就彻底失效了。
浏览器同源时 Cookie 会自动发送,但需满足三个硬性条件
同源(如页面是 https://example.com,WS 地址是 wss://example.com:8080)只是前提,真正决定 Cookie 是否到达服务端的是以下三项:
-
Secure属性必须存在:使用wss://时,Cookie 若没带Secure,浏览器直接忽略 -
SameSite不能是Strict:推荐设为Lax;若跨子域(如app.example.com→ws.example.com),则需SameSite=None; Secure -
Domain和Path必须匹配:比如Domain=example.com覆盖wss://api.example.com,而Path=/auth就不匹配/ws路径
Gorilla WebSocket 中必须在 upgrader.Upgrade() 前读取 Cookie
很多人把验证逻辑写在 conn 建立后,这是错的 —— 此时 HTTP 请求上下文已销毁,req.Cookie("session_id") 必然 panic 或返回 http.ErrNoCookie。
正确位置是 handler 函数体内、调用 upgrader.Upgrade() 之前:
r.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
if !isValidSession(cookie.Value) {
http.Error(w, "Invalid session", http.StatusUnauthorized)
return
}
// ✅ 此刻才升级
conn, _ := upgrader.Upgrade(w, r, nil)
defer conn.Close()
})
跨域场景下 Cookie 不会自动发,必须前后端双向配合
前端页面在 https://admin.example.com,WebSocket 连到 wss://api.example.com —— 这属于跨域,浏览器默认不带 Cookie,除非你显式开启凭证支持。
- 前端不用改
new WebSocket()构造函数(它不支持传credentials选项),而是确保页面本身已通过fetch或表单登录,并且服务端 Set-Cookie 时设置了SameSite=None; Secure - 服务端响应握手请求时,必须返回
Access-Control-Allow-Origin: https://admin.example.com+Access-Control-Allow-Credentials: true;缺任意一个,浏览器就屏蔽 Cookie - 注意:
Access-Control-Allow-Origin不能是通配符*,否则credentials会被浏览器强制忽略
Node.js ws 库要用 verifyClient 钩子,别依赖 upgrade 后的 connection
ws 库的 verifyClient 是唯一安全可读取原始 HTTP 请求头的地方。一旦进入 connection 事件,info.req 已被销毁或不可靠。
-
info.req.headers.cookie是原始字符串,需手动解析(生产环境建议用cookie包,别用.split('; ')简单切分) - 不要在
verifyClient里做耗时操作(如查数据库),否则会阻塞整个握手队列;可先校验签名,再异步加载用户信息并挂载到info.user - 返回
done(false, 401)会直接断开连接,状态码和消息会出现在浏览器 DevTools 的 WebSocket 请求详情中,方便定位
最容易被忽略的一点:所有 Cookie 校验逻辑都发生在 HTTP 层,和 WebSocket 协议本身无关。这意味着,任何基于 HTTP 的中间件(如反向代理、WAF、CDN)都可能提前终止请求、重写头、或剥离 Cookie —— 务必确认它们没有干扰原始 Cookie 请求头。


















