Go Web服务需显式处理CORS,推荐用rs/cors中间件;手动设置易漏预检请求和凭证支持;Gin需禁用默认配置,显式指定AllowedOrigins与AllowCredentials;预检失败主因是无OPTIONS路由匹配。
Go Web 服务默认不处理
跨域
请求,
浏览器
会直接拦截
或
的响应(
状态码
看似 200,但控制台报
)。必须显式添加
等响应头,且需覆盖预检(
)请求。
使用
中间件最稳妥
这是社区事实标准,兼容
、
、
等主流框架,能正确处理简单请求和预检请求,避免手动漏掉
或
等关键头。
常见错误是只写
却没允许凭证(
),导致浏览器拒绝响应。此时必须指定具体域名,且需开启
:
立即学习
“
go语言免费学习笔记(深入)
”;
注意:
不能同时设为
和
,否则 Go 会 panic,浏览器也会拒绝。
手动加 Header 的陷阱
手动设置响应头看似简单,但极易出错。尤其容易忽略以下三点:
对
请求不做处理,导致预检失败
未在所有
路由
统一设置,某些 handler 忘记写
把
写成固定值却没校验
请求头,引发安全风险
若坚持手写,至少保证:
Comet AI 浏览器
下载 Comet AI 浏览器,体验由 Perplexity AI 驱动的革命性上网方式。内置 AI 助手可实时总结网页、跨标签页对比信息、自动执行任务。告别繁琐操作,让 AI 成为你的浏览副驾,大幅提升研究与工作效率。支持 Windows、macOS、Android 和 iOS。
下载
}
Gin 框架的
配置要点
Gin 自带
,但默认配置(如
)仅适用于开发环境。生产中必须显式控制
和
:
允许任意源 +
,但禁用凭证,无法用于登录态保持
同样禁止
必须用
列表 +
组合,并确保
前端
设置
示例:
预检请求(
)为什么总被 404?
根本原因是:没有路由匹配到
方法。Gin 默认不注册
路由;
的
也不自动响应;即使写了
,中间件也可能未生效。
解决方式只有两个:
用
或
—— 它们会在内部拦截并响应预检
手动注册通配
路由(不推荐):
,但需确保它在所有中间件链最前
别试图在 handler 里用
就返回,那只能覆盖当前路由,无法解决全站预检问题。
fetchXMLHttpRequestBlocked by CORS policyAccess-Control-Allow-OriginOPTIONSrs/corsnet/httpginechoAccess-Control-Allow-HeadersAccess-Control-Max-AgeAccess-Control-Allow-Origin: *withCredentials: trueAllowCredentialsimport "github.com/rs/cors"
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
// 允许带凭证的跨域请求
corsHandler := cors.New(cors.Options{
AllowedOrigins: []string{"https://www.php.cn/link/b05edd78c294dcf6d960190bf5bde635"},
AllowCredentials: true,
AllowedHeaders: []string{"Content-Type", "Authorization", "X-Requested-With"},
}).Handler(handler)
http.ListenAndServe(":8080", corsHandler)
AllowedOrigins*AllowCredentials: truenet/httpOPTIONSw.Header().Set(...)Access-Control-Allow-OriginOriginfunc corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Expose-Headers", "X-Total-Count")
w.Header().Set("Access-Control-Allow-Credentials", "true")
}
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})Cors()github.com/gin-contrib/corscors.Default()AllowedOriginsAllowCredentialscors.Default()*cors.Config{AllowAllOrigins: true}AllowCredentials: trueAllowedOriginsAllowCredentials: truefetchcredentials: 'include'import "github.com/gin-contrib/cors"
r := gin.Default()
r.Use(cors.New(cors.Config{
AllowedOrigins: []string{"https://www.php.cn/link/c5e212421256cf0dcf335940944bc162"},
AllowCredentials: true,
AllowHeaders: []string{"Content-Type", "Authorization"},
ExposeHeaders: []string{"X-Total-Count"},
MaxAge: 24 * time.Hour,
}))
OPTIONSOPTIONSOPTIONSnet/httpServeMuxrouter.OPTIONS("/api/user", ...)rs/corsgin-contrib/corsOPTIONSr.OPTIONS("/*path", func(c *gin.Context) {})if r.Method == "OPTIONS"