WebSocket跨域本质是握手阶段Origin校验,非CORS预检;403错误表明服务端拒绝升级,需Nginx透传Upgrade/Connection头、Spring Boot用HandshakeInterceptor校验Origin,且Origin值必须严格匹配。
WebSocket握手阶段被拦截,不是CORS预检问题
WebSocket
跨域
和HTTP CORS根本不是一回事:它没有OPTIONS预检,只在HTTP Upgrade握手阶段检查
头,并依赖服务端返回
响应。浏览器控制台报错如
,说明服务端拒绝了握手请求,而不是响应头没加对。
常见误操作包括:
给WebSocket服务硬塞
响应头——这无效,因为握手成功后就不是HTTP了
用Nginx转发但没开启
和
透传
Spring Cloud Gateway里配了
却没关掉只读headers限制,直接抛
Nginx反向代理WebSocket必须透传Upgrade头
如果用Nginx做入口,只加
完全没用。关键是要让Upgrade请求原样抵达后端服务。
必须配置以下三项(缺一不可):
(启用HTTP/1.1,否则不支持Upgrade)
(把客户端的
头传下去)
(显式声明连接升级,不能写
,否则可能被覆盖成
)
漏掉任意一条,Nginx会按普通HTTP处理,返回200或404,导致WebSocket握手失败。
Spring Boot原生WebSocket需自定义HandshakeInterceptor
Spring Boot默认不校验
,但生产环境必须校验。不能靠
注解——它只对HTTP接口生效,对WebSocket握手无效。
正确做法是实现
:
PHP数据库链接类(PDO+Access)
PHP数据库链接类(PDO+Access)
下载
重写
方法,从
取来源
白名单比对后,调用
(部分老客户端需要)
务必返回
放行,返回
会直接拒绝握手
注意:
在这里毫无意义,别加。
Websocat或自建服务要设custom-reply-headers
像Websocat这类轻量级WebSocket服务,没有内置CORS逻辑,必须手动注入响应头。
启动命令中必须带
参数:
基础可用:
若需凭证(如cookie),必须同时加
,且Origin不能为
生产环境禁止用
通配符,必须精确匹配前端域名,否则
会失效
这个头只在握手响应里起作用,后续帧通信不走HTTP,所以不用再管其他头。
最易被忽略的一点:所有方案都要求前后端
值严格一致——https://a.com 和 https://a.com/ 结尾斜杠不同,也算跨域;localhost:3000 和 127.0.0.1:3000 也不同源。别只盯着头,先核对浏览器发出来的
到底是什么。
OriginHTTP 101 Switching ProtocolsError during WebSocket handshake: Unexpected response code: 403Access-Control-Allow-OriginUpgradeConnectionCorsWebFilterUnsupportedOperationExceptionadd_header Access-Control-Allow-Origin *proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;Upgrade: websocketproxy_set_header Connection "upgrade";$http_connectioncloseOrigin@CrossOriginHandshakeInterceptorbeforeHandshakerequest.getHeaders().getOrigin()response.getHeaders().set("Sec-WebSocket-Origin", origin)truefalseresponse.setHeader("Access-Control-Allow-Origin", "*")--custom-reply-headerswebsocat ws-l:0.0.0.0:8080 --custom-reply-headers "Access-Control-Allow-Origin: https://your-app.com"Access-Control-Allow-Credentials: true**withCredentials: trueOriginOrigin