Go WebSocket连接被拒本质是gorilla/websocket的CheckOrigin默认返回false,需显式覆盖该函数;其与HTTP CORS机制无关,必须在Upgrader中配置而非中间件。

Go服务端报 origin not allowed by Upgrader.CheckOrigin,本质不是前端“跨域”,而是 WebSocket 服务端显式拒绝了连接请求——CheckOrigin 默认返回 false,必须主动覆盖。
为什么 gorilla/websocket 默认拦掉所有 origin
gorilla/websocket 的 Upgrader.CheckOrigin 是一个安全钩子,默认实现是:
func (u *Upgrader) CheckOrigin(r *http.Request) bool {
return false
}
它不看请求头里的 Origin 值,直接拒掉,防止未授权的 WebSocket 连接被滥用。这不是 bug,是设计使然。
- 即使前端地址是
http://localhost:3000,没配CheckOrigin就会触发该错误 - 这个检查发生在 HTTP 升级阶段,早于你的 handler 执行,所以中间件或路由层加 CORS 头完全无效
- 和 HTTP CORS(
Access-Control-Allow-Origin)是两套机制,不能混用或互相替代
Go WebSocket 跨域最简修复:重写 CheckOrigin
只需在初始化 websocket.Upgrader 时提供一个允许逻辑,常见做法有三种:
- 开发环境快速验证:
CheckOrigin: func(r *http.Request) bool { return true }(仅限本地调试) - 白名单匹配(推荐):
CheckOrigin: func(r *http.Request) bool { return r.Header.Get("Origin") == "http://localhost:3000" || r.Header.Get("Origin") == "https://prod.example.com" } - 支持凭证且需动态判断:
CheckOrigin: func(r *http.Request) bool { origin := r.Header.Get("Origin"); return origin == "https://app.example.com" && r.URL.Query().Get("token") != "" }
注意:CheckOrigin 函数里不能调用 w.WriteHeader() 或写响应体,它只做布尔判断。
GIN 框架里别在 handler 里配 Upgrader
很多人在 GIN 的某个路由 handler 里 new 一个 Upgrader 并设 CheckOrigin,这看似可行,但极易出问题:
- 多个 handler 各自 new,导致内存泄漏或配置不一致
- 没复用
Upgrader实例,ReadBufferSize/WriteBufferSize等参数每次重设,影响性能 - Gin 的
c.Writer和原生http.ResponseWriter类型不完全兼容,某些版本下升级失败静默
正确做法是全局声明一个复用的实例:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return r.Header.Get("Origin") == "http://localhost:3000"
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
func wsHandler(c *gin.Context) {
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
return
}
defer conn.Close()
// ...
}
OPTIONS 预检对 WebSocket 无效,别浪费时间配它
这是最容易踩的误区:试图给 WebSocket 路由加 HTTP CORS 中间件、拦截 OPTIONS、返回 200 —— 完全没用。
- WebSocket 连接走的是
GET+Upgrade: websocket,不发 OPTIONS 预检 - 浏览器对 WebSocket 的跨域控制,只查
Upgrader.CheckOrigin返回值,不看响应头里的Access-Control-* - 如果你同时暴露了 REST API 和 WebSocket,CORS 中间件管 HTTP,
CheckOrigin管 WS,二者必须分开配,不能指望一个方案通吃
真正要验证是否生效,用 curl 直连 WebSocket 升级接口,观察是否返回 101 Switching Protocols,而不是 403 或空响应。


















