如何实现Golang中的跨域资源共享(CORS)?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1090个文字,预计阅读时间需要5分钟。
在Go语言中,若HTTP服务默认不处理CORS,浏览器将无法直接访问。具体表现为,浏览器会显示缺少`Access-Control-Allow-Origin`标签。这通常意味着请求被直接截断,并非后端未收到请求,而是根本未发起请求。需要手动添加逻辑,并且确保OPTIONS请求得到正确处理。
怎么写一个能用的 CORS 中间件(net/http)
手写中间件最轻量,适合简单场景,但三个点不能错:
- 所有请求(包括非 OPTIONS)都要设
Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers -
r.Method == "OPTIONS"必须立即返回http.StatusNoContent(204),且不能调用next.ServeHTTP() - 如果前端用了
fetch({ credentials: 'include' }),Access-Control-Allow-Origin不能是"*",得写死具体域名,比如"https://myapp.com",同时必须加Access-Control-Allow-Credentials: "true"
示例片段:
func 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-Credentials", "true") w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With") w.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") } if r.Method == "OPTIONS" { w.WriteHeader(http.StatusNoContent) return } next.ServeHTTP(w, r) }) }
为什么 gin-contrib/cors 比手写更稳
手写中间件容易在 AllowCredentials 和 AllowOrigins 配置上翻车:只要 AllowCredentials: true 且 AllowOrigins 含 "*",浏览器就静默丢弃响应,Network 面板里都看不到完整失败原因。
立即学习“go语言免费学习笔记(深入)”;
-
gin-contrib/cors自动校验二者互斥关系,AllowOrigins设为"*"时会强制禁用AllowCredentials - 它自动拦截并响应 OPTIONS 请求,不触发后续 handler(避免数据库误写等副作用)
- 支持
MaxAge缓存预检结果,减少重复 OPTIONS 往返 - 必须在
r.Use()中注册,且要在任何r.GET()等路由定义之前,否则中间件不生效
配置示例:
r.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://myapp.com", "http://localhost:3000"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Content-Type", "Authorization"}, AllowCredentials: true, ExposeHeaders: []string{"X-Total-Count"}, MaxAge: 12 * 60 * 60, }))
gorilla/handlers.CORS 为什么常被选作生产方案
它比 gin-contrib/cors 更通用,适配 net/http、echo、fiber 等任意基于 http.Handler 的框架,且对 header 设置时机、OPTIONS 响应体、缓存控制做了更细粒度封装。
- 若用
handlers.AllowedOrigins([]string{"*"}),它内部会自动跳过AllowCredentials,避免浏览器报错 -
handlers.ExposedHeaders([]string{"X-Request-ID"})是显式白名单,没列的 header 前端 JS 拿不到 - 注意:Nginx 后面跑 Go 服务时,必须配置
add_header Access-Control-Allow-Origin $sent_http_access_control_allow_origin always;,否则 Nginx 会清空 Go 写的 header
跨域失败时先盯住这三个地方
90% 的问题出在这三处,别一上来就改代码:
- 浏览器 Network 面板里看 OPTIONS 请求是否返回 204/200;如果返回 404 或 405,说明路由没覆盖到 OPTIONS,中间件没注册对位置
- 检查响应头里有没有
Access-Control-Allow-Origin,如果有但值是"*",而前端又带了 cookie,那这个响应会被浏览器直接丢弃 - 用 curl 模拟预检:
curl -I -X OPTIONS -H "Origin: https://myapp.com" -H "Access-Control-Request-Method: POST" http://localhost:8080/api/user,看返回头是否齐全
真正难的不是加几行 header,而是理解浏览器预检机制和凭证模式之间的硬性约束——这些规则由浏览器强制执行,Go 本身无权绕过。
本文共计1090个文字,预计阅读时间需要5分钟。
在Go语言中,若HTTP服务默认不处理CORS,浏览器将无法直接访问。具体表现为,浏览器会显示缺少`Access-Control-Allow-Origin`标签。这通常意味着请求被直接截断,并非后端未收到请求,而是根本未发起请求。需要手动添加逻辑,并且确保OPTIONS请求得到正确处理。
怎么写一个能用的 CORS 中间件(net/http)
手写中间件最轻量,适合简单场景,但三个点不能错:
- 所有请求(包括非 OPTIONS)都要设
Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers -
r.Method == "OPTIONS"必须立即返回http.StatusNoContent(204),且不能调用next.ServeHTTP() - 如果前端用了
fetch({ credentials: 'include' }),Access-Control-Allow-Origin不能是"*",得写死具体域名,比如"https://myapp.com",同时必须加Access-Control-Allow-Credentials: "true"
示例片段:
func 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-Credentials", "true") w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With") w.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") } if r.Method == "OPTIONS" { w.WriteHeader(http.StatusNoContent) return } next.ServeHTTP(w, r) }) }
为什么 gin-contrib/cors 比手写更稳
手写中间件容易在 AllowCredentials 和 AllowOrigins 配置上翻车:只要 AllowCredentials: true 且 AllowOrigins 含 "*",浏览器就静默丢弃响应,Network 面板里都看不到完整失败原因。
立即学习“go语言免费学习笔记(深入)”;
-
gin-contrib/cors自动校验二者互斥关系,AllowOrigins设为"*"时会强制禁用AllowCredentials - 它自动拦截并响应 OPTIONS 请求,不触发后续 handler(避免数据库误写等副作用)
- 支持
MaxAge缓存预检结果,减少重复 OPTIONS 往返 - 必须在
r.Use()中注册,且要在任何r.GET()等路由定义之前,否则中间件不生效
配置示例:
r.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://myapp.com", "http://localhost:3000"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Content-Type", "Authorization"}, AllowCredentials: true, ExposeHeaders: []string{"X-Total-Count"}, MaxAge: 12 * 60 * 60, }))
gorilla/handlers.CORS 为什么常被选作生产方案
它比 gin-contrib/cors 更通用,适配 net/http、echo、fiber 等任意基于 http.Handler 的框架,且对 header 设置时机、OPTIONS 响应体、缓存控制做了更细粒度封装。
- 若用
handlers.AllowedOrigins([]string{"*"}),它内部会自动跳过AllowCredentials,避免浏览器报错 -
handlers.ExposedHeaders([]string{"X-Request-ID"})是显式白名单,没列的 header 前端 JS 拿不到 - 注意:Nginx 后面跑 Go 服务时,必须配置
add_header Access-Control-Allow-Origin $sent_http_access_control_allow_origin always;,否则 Nginx 会清空 Go 写的 header
跨域失败时先盯住这三个地方
90% 的问题出在这三处,别一上来就改代码:
- 浏览器 Network 面板里看 OPTIONS 请求是否返回 204/200;如果返回 404 或 405,说明路由没覆盖到 OPTIONS,中间件没注册对位置
- 检查响应头里有没有
Access-Control-Allow-Origin,如果有但值是"*",而前端又带了 cookie,那这个响应会被浏览器直接丢弃 - 用 curl 模拟预检:
curl -I -X OPTIONS -H "Origin: https://myapp.com" -H "Access-Control-Request-Method: POST" http://localhost:8080/api/user,看返回头是否齐全
真正难的不是加几行 header,而是理解浏览器预检机制和凭证模式之间的硬性约束——这些规则由浏览器强制执行,Go 本身无权绕过。

