MaxAge必须显式设为整数秒值(如3600),否则默认为0导致每次非简单请求都触发OPTIONS预检;需确保响应头含Access-Control-Max-Age且未被中间件或代理覆盖。

MaxAge 配置必须显式设值,否则浏览器不缓存预检结果
Echo 的 CORSWithConfig 默认 MaxAge 为 0,意味着每次非简单请求前都要发一次 OPTIONS 预检——这在高并发场景下会显著放大后端压力。浏览器只有收到带 Access-Control-Max-Age 响应头的预检响应,才会缓存该规则。
实操建议:
-
MaxAge单位是秒,常见合理值为86400(24 小时)或3600(1 小时),别用负数或超大值(如31536000),部分旧版 Chrome 会忽略 - 必须配合
AllowMethods和AllowHeaders使用,否则即使设了MaxAge,预检响应也可能被浏览器判定为无效而拒绝缓存 - 若前端实际只用
GET和POST,就别把PUT、DELETE写进AllowMethods,否则预检缓存会因方法集变大而更易失效
中间件注册顺序影响 MaxAge 是否生效
如果你在 e.Use(middleware.CORSWithConfig(...)) 之前注册了自定义日志或鉴权中间件,且它们对 OPTIONS 请求做了提前响应(比如调用了 c.NoContent() 或 w.WriteHeader(200)),那 CORS 中间件根本不会执行,MaxAge 自然没机会写入响应头。
检查点:
- 用
curl -I -X OPTIONS http://localhost:8080/api/foo看响应头里有没有Access-Control-Max-Age - 确保所有中间件在
next(c)前不终止请求生命周期;尤其注意 JWT 验证中间件对 OPTIONS 的放行逻辑 - 如果用了自定义 OPTIONS 处理(比如统一兜底路由),务必删掉或改用
c.Next()交由 CORS 中间件接管
MaxAge 不起作用的典型现象和定位方式
浏览器开发者工具 Network 标签页中,每个非简单请求前都重复出现 OPTIONS 请求,且响应头缺失 Access-Control-Max-Age,说明配置未生效。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
常见原因:
-
CORSWithConfig传参时写成MaxAge: "3600"(字符串)而非MaxAge: 3600(整数),Go 编译不报错但 JSON tag 解析失败,字段被忽略 -
AllowOrigins设为"*"但同时开了AllowCredentials: true,整个 CORS 配置被浏览器静默丢弃,连带MaxAge失效 - 反向代理(如 Nginx)覆盖了响应头,需确认代理层未清除或重写
Access-Control-Max-Age
真正起作用的配置长这样:
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"https://myapp.com"},
AllowMethods: []string{echo.GET, echo.POST, echo.OPTIONS},
AllowHeaders: []string{echo.HeaderContentType, echo.HeaderAuthorization, "X-Requested-With"},
AllowCredentials: true,
MaxAge: 3600,
}))
MaxAge 是个“信任型”配置:它不改变跨域是否允许,只影响浏览器要不要反复问。一旦配错,问题藏得深,往往要抓包看 OPTIONS 响应头才能确认。










