websocket 403错误主因是握手请求被拦截,非权限问题:nginx需透传upgrade和connection头;spring boot须显式配置setallowedorigins;go用gorilla需自定义checkorigin;前端请求头缺失或cdn/waf静默丢弃upgrade头亦常见。

WebSocket 403 错误不是权限不足,而是握手被拦在了半路——服务器根本没收到完整的 Upgrade 请求,或者拒绝了 Origin 校验。直接改配置就能解决,但错一个 header 就白忙活。
nginx 反向代理必须透传 Upgrade 和 Connection 头
Nginx 默认不转发 Upgrade 和 Connection 这两个关键 header,导致后端永远收不到“我要升级成 WebSocket”的信号,只能返回 403 或 400。
在 location 块里补全这四行:
proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host;
注意点:
-
$http_upgrade是 nginx 内置变量,大小写敏感,不能写成$http_Ugrade -
Connection "upgrade"的引号不能省,否则 nginx 会把它当普通字符串处理 - 如果用了
proxy_pass https://...,还要加proxy_ssl_verify off;(仅测试环境)避免证书拦截
Spring Boot 必须显式放行 Origin 并关闭默认拦截器
Spring WebSocket 默认启用 OriginHandshakeInterceptor,且只允许同源,连 localhost:3000 访问 localhost:8080 都会被拒。
两种可靠写法:
方式一:用 setAllowedOrigins(推荐)
registry.addHandler(new MyWebSocketHandler(), "/ws")
.setAllowedOrigins("https://app.example.com", "http://localhost:3000");
方式二:彻底禁用 Origin 检查(开发阶段快)
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
registry.addHandler(new MyWebSocketHandler(), "/ws")
.setAllowedOrigins("*"); // 注意:生产环境禁用 *
关键细节:
-
setAllowedOrigins必须在addHandler之后立即调用,顺序错就无效 - 如果用了 Spring Security,还要确保
/ws/**路径没被http.authorizeHttpRequests()拦截 -
*在 Spring Boot 2.6+ 中已不支持凭据模式(withCredentials: true),此时必须列明具体域名
Go 语言用 gorilla/websocket 时要关掉 Origin 校验
gorilla/websocket 默认校验 Origin 头,本地调试时浏览器发的 Origin: null 或 file:// 会被直接 403。
最简修复是自定义 CheckOrigin 函数:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // 开发阶段可临时放开
},
}
生产环境建议改成白名单:
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("Origin")
return origin == "https://app.example.com" || origin == "http://localhost:3000"
}
注意:
- 别用老版
code.google.com/p/go.net/websocket,它不支持CheckOrigin配置 - 如果前端带 cookie,
CheckOrigin返回 true 后,还需设置Upgrader.CheckOrigin+http.SetCookie配合
浏览器控制台报 403 但服务端没日志?先抓包看真实请求头
很多 403 实际发生在 nginx 层或 CDN 层,后端压根没收到请求。光看服务端日志会漏掉关键信息。
快速定位方法:
- Chrome DevTools → Network → 找到 ws 请求 → 点开 → Headers → 看 Request Headers 里有没有
Upgrade: websocket和Connection: Upgrade - 如果有,说明前端发出了,问题在代理或后端;如果没有,说明前端代码或构建工具(如 vite、webpack dev server)本身做了拦截
- vite 开发服务器默认不代理 WebSocket,需手动配
server.proxy['/ws'] = { ws: true }
真正麻烦的从来不是配置项本身,而是某一层(比如 CDN、WAF、k8s ingress)悄悄吃了 Upgrade 头,还假装转发成功。遇到这种,优先在 nginx access_log 里加 $upstream_http_upgrade 字段确认头是否抵达后端。










