WebSocket握手返回403而非CORS错误,是因为浏览器仅在HTTP握手阶段检查101响应,而403表明服务器或中间件(如Nginx、Spring Security、网关)拦截了Upgrade请求;常见原因包括Nginx未透传Upgrade/Connection头、Security未放行/ws/info等端点、网关未配置ws://协议及重复CORS头。

WebSocket 连接返回 403 Forbidden,基本不是跨域(CORS)问题本身,而是服务器拒绝了协议升级请求——关键在 Upgrade 和 Connection 头没被正确透传或校验失败。
为什么 WebSocket 握手会返回 403 而不是 CORS 错误?
浏览器对 WebSocket 的“跨域检查”只发生在 HTTP 握手阶段(即首次 GET 请求),此时它只看响应是否含 101 Switching Protocols。如果服务端或中间件(如 Nginx、Spring Security、Gateway)拦截了这个请求并返回 403,说明它根本没走到 WebSocket 协议升级逻辑里。
常见诱因包括:
- Nginx 默认不转发
Upgrade和Connection头,导致后端收不到升级意图 - Spring Security 拦截了
/ws/**路径,且未显式放行 WebSocket 握手端点(如/ws/info) - 网关(如 Spring Cloud Gateway)同时配置了全局 CORS 和 WebSocket 路由,造成响应头冲突(如两个
Access-Control-Allow-Origin) - 前端连接 URL 中路径与后端注册的
@ServerEndpoint或 STOMPaddEndpoint不一致,触发 403 权限拒绝而非 404
Nginx 反向代理必须加的三行配置
仅靠 proxy_pass 不足以支持 WebSocket;必须显式透传协议升级信号:
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}注意:proxy_http_version 1.1 是强制要求(HTTP/1.0 不支持 Upgrade);$http_upgrade 是 Nginx 内置变量,会原样取客户端发来的 Upgrade: websocket 值;Connection "upgrade" 必须用双引号包裹字符串,否则 Nginx 会报语法错误。
漏掉任意一行,Nginx 就会以普通 HTTP 方式转发请求,后端收不到升级头,大概率返回 403 或 404。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
Spring Boot + STOMP 场景下必须配的两项
STOMP over WebSocket 的握手分两步:先请求 /ws/info(SSE 风格),再建立真正 WebSocket 连接。二者都要放行:
- HTTP 层:用
CorsConfiguration允许前端 origin,并设allowCredentials = true(若需带 Cookie) - Security 层:用
HttpSecurity显式忽略握手端点和 WebSocket 路径,例如:
http.authorizeHttpRequests(authz -> authz
.requestMatchers("/ws/info", "/ws/**").permitAll()
.anyRequest().authenticated()
);⚠️ 如果只配了 CORS 但没在 Security 放行 /ws/info,Spring Security 会直接返回 403 —— 此时浏览器控制台看不到 CORS 报错,只有 Network 面板显示 GET /ws/info 403。
网关(如 Spring Cloud Gateway)转发 WebSocket 的坑
Gateway 默认把所有请求当 HTTP 处理,WebSocket 路由需额外声明协议类型:
- 路由
uri必须以lb:ws://或ws://开头(不能是lb:http://) - 全局 CORS 配置(
globalcors)必须关闭或排除 WebSocket 路径,否则会注入重复的Access-Control-Allow-Origin,触发浏览器拒绝 - 推荐做法:删掉 Gateway 的全局 CORS,只在后端应用层(如 Spring WebSocket 配置)做跨域控制
典型错误配置:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': # ← 这里匹配了 /ws/**,导致握手响应带两个 Origin 头
allowedOriginPatterns: "*"修正方式:把 '[/**]' 改为 '[/api/**]' 等业务 API 前缀,明确排除 WebSocket 路径。
真正卡住人的地方,往往不是“怎么加 CORS 头”,而是没意识到 403 来自协议升级被拒——得顺着请求链路一层层确认:Nginx 是否透传 Upgrade?网关是否识别 ws:// 协议?Security 是否放行了 info 端点?路径是否拼写一致?这些点任何一个断掉,都会静默返回 403。

















