必须显式拦截options请求并返回204,否则后端可能返回405导致cors失败;需动态设置access-control-allow-origin(禁用*)、启用credentials、配置access-control-max-age≥86400,并确保add_header在生效location块内且不被覆盖。

直接在 Nginx 层加响应头就能解决,不用动后端代码,也不用前端配 devServer proxy —— 但必须处理好 OPTIONS 预检请求、凭证支持和缓存控制,否则配置写了也白写。
为什么加了 add_header 还是报 CORS 错误
常见原因是响应头没生效或被覆盖。Nginx 的 add_header 指令只对**成功返回的响应(2xx/3xx)** 生效,而 OPTIONS 请求如果没显式处理,可能落到后端并返回 405 或 502,此时 Nginx 不会插入头;或者多个 location 块嵌套导致 header 被后加载的块覆盖。
实操建议:
-
add_header必须放在能实际返回响应的location块里,不要只写在父块中 - 用
curl -I -X OPTIONS http://your-domain/api/xxx确认OPTIONS返回 204 且带全 CORS 头 - 检查是否有其他模块(如
more_set_headers)或后端服务覆盖了 header - 避免在
if块外直接写add_header处理非 2xx 响应 —— 它不生效
如何正确处理 OPTIONS 预检请求
浏览器对非简单请求(如带 Authorization、Content-Type: application/json)会先发 OPTIONS,若该请求没被 Nginx 拦截并快速响应,后端通常不支持或返回 405,CORS 就失败。
实操建议:
- 在对应
location块中加if ($request_method = 'OPTIONS') { return 204; } - 确保
return 204前没有proxy_pass,否则请求仍会转发 - 204 响应体为空,但必须带所有 CORS 头 —— 所以
add_header要写在if同级位置(Nginx 会继承) - 不要用
rewrite ... last处理 OPTIONS,容易绕过 header 插入逻辑
带 Cookie 的跨域必须注意的三个限制
当前端设了 credentials: 'include',后端(或 Nginx)必须同时满足:指定具体源、开启凭证支持、且不能用通配符 * —— 否则浏览器直接拒绝解析响应。
实操建议:
-
add_header 'Access-Control-Allow-Origin' 'https://your-frontend.com';(不能是*) add_header 'Access-Control-Allow-Credentials' 'true';- 前端发起请求时必须带
withCredentials: true(fetch)或xhrFields: { withCredentials: true }(jQuery) - 如果前端域名不固定(如多环境共用一套 Nginx),可用变量动态设置 Origin:
add_header 'Access-Control-Allow-Origin' $http_origin;,但需配合白名单校验逻辑(用map指令)
开发 vs 生产环境的配置差异点
本地调试常图省事设 Access-Control-Allow-Origin: *,但上线后必须收紧 —— 尤其涉及登录态时,放行所有源等于放弃安全边界。
实操建议:
- 测试环境可接受
*+Access-Control-Allow-Methods: *,但禁用Access-Control-Allow-Credentials - 生产环境必须硬编码可信域名,或用
map构建白名单:map $http_origin $cors_origin { default ""; "https://app.example.com" "https://app.example.com"; "https://admin.example.com" "https://admin.example.com"; } add_header 'Access-Control-Allow-Origin' $cors_origin; -
Access-Control-Max-Age设为 86400(24 小时)以上可减少预检请求频次,但修改策略后需等缓存过期才能生效
最容易被忽略的是:Nginx 配置 reload 后,旧 worker 进程可能还在处理请求,CORS 头变更不会立即体现;还有就是 HTTPS 环境下前端地址用了 http:// 却期望被 https:// 源匹配,这种协议不一致会导致 Origin 校验失败 —— 看似配对了,其实根本对不上。











