必须在 nginx 的 options 响应中使用 add_header access-control-max-age 1728000 always; 并搭配 cache-control 和 expires,且需在拦截块内 return 204 前设置,否则预检无法缓存。

直接在 Nginx 的 OPTIONS 响应中设置 Access-Control-Max-Age,能让浏览器缓存预检结果,避免重复发 OPTIONS 请求。关键不是“设了就行”,而是设对位置、设对值、设对方式。
必须加 always 参数才生效
Nginx 默认只在 2xx/3xx 响应里注入 `add_header`,而 `return 204` 是无 body 的终止响应,不触发默认逻辑。 如果不加 `always`,浏览器收不到 `Access-Control-Max-Age`,每次请求都得重做预检。- 正确写法:
add_header Access-Control-Max-Age 1728000 always; - 错误写法:
add_header Access-Control-Max-Age 1728000;(没 always,OPTIONS 响应里压根没有这个头)
推荐值:1728000 秒(20 天)
- 默认值通常只有 5 分钟(300 秒),高频接口可能每分钟触发多次预检; - 设为 1728000 覆盖绝大多数用户会话周期,实测稳定可靠; - 不建议设为无限(如 `max-age=31536000`),因域名变更或策略调整后旧缓存无法及时失效; - 若业务要求更短控制粒度,可设为 86400(24 小时),但低于 3600(1 小时)就失去缓存意义。要搭配标准缓存头一起用
仅靠 `Access-Control-Max-Age` 不够,CDN 或中间代理可能忽略它。需同步设置 HTTP 缓存指令:add_header Cache-Control "public, max-age=1728000" always;expires 1728000s;(比用 `$time_iso8601` 更简洁可靠)- 若启用了
proxy_cache,确认该 location 下:proxy_cache_bypass off;和proxy_no_cache off;,防止被缓存策略绕过。
必须放在 OPTIONS 拦截块里,不能靠后端回传
预检响应必须由 Nginx 在进入 upstream 前就生成并返回,不能依赖后端服务:- ✅ 正确结构:用独立
location或if块拦截OPTIONS,return 204前完成所有add_header ... always; - ❌ 错误结构:
if ($request_method = 'OPTIONS') { proxy_pass ... }—— 这会让请求穿透到后端,完全失去预检缓存意义; - ⚠️ 注意:
if只能出现在server或location内,且不能和proxy_pass同级混用(易导致 header 丢失或 502)。
不复杂但容易忽略











