根本原因是返回200而非规范要求的204 no content,且未正确abort终止中间件链;必须设置合法响应头、调用c.abortwithstatus(204)并立即return,避免后续执行。

为什么 OPTIONS 请求卡住或返回 200 却没响应?
根本原因不是 Gin 慢,而是你返回了 c.AbortWithStatus(200) —— 这违反了 CORS 预检规范。浏览器要求预检成功必须返回 204 No Content,且不能带响应体。返回 200 会触发部分浏览器(Chrome/Safari)静默终止后续请求,看起来“卡住”了。
正确写法:OPTIONS 必须 204 + Abort + 立即退出
中间件里遇到 OPTIONS 方法时,要一次性设置头、返回状态、终止链,三步缺一不可:
- 用
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)设置合法源(不能是"*"如果开了credentials) - 调用
c.Status(204)或c.AbortWithStatus(204),确保无响应体 - 必须跟
c.Abort(),否则后续中间件和路由 handler 还会执行
示例片段:
if c.Request.Method == "OPTIONS" {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.AbortWithStatus(204)
return
}
gin.Default() 自带的 Recovery 中间件会干扰 OPTIONS?
会。默认 Recovery 中间件在 panic 时写 HTML 响应,而 204 不该有 body;若 OPTIONS 路径下某中间件 panic,Recovery 可能强行写入内容,破坏 204 语义。
- 生产环境建议用
gin.New()替代gin.Default() - 手动注册必要中间件:
r.Use(Logger()),但跳过Recovery(),或确保它不介入 OPTIONS 流程 - 更稳妥做法:把 CORS 中间件放在最前面(
r.Use(CORSMiddleware())),靠前拦截并终结 OPTIONS
动态 Origin 校验容易漏掉空字符串或 localhost 端口
c.Request.Header.Get("Origin") 在非跨域请求(如 curl 直接调)时返回空,此时不应设 Access-Control-Allow-Origin,否则可能引发浏览器报错。
- 检查
origin != ""再做白名单匹配 - 开发时常见漏配:
http://localhost:3000和http://127.0.0.1:3000是两个不同源,都要列进白名单 - 生产环境别硬编码,应从配置或环境变量读取允许源列表
真正让 OPTIONS 极速返回的关键,不是优化路由树,而是避免任何多余计算、不写 body、不走后续 handler —— 它本就不该进业务逻辑层。











