不能通过http块的add_header实现真正有效的跨域全局策略,因其不继承、不生效于实际请求;必须在每个location块内显式配置add_header always及OPTIONS预检拦截,并配合map实现动态Origin匹配。

不能通过 http 块的 add_header 实现真正有效的跨域全局策略。
add_header 在 http 块中不生效于实际请求
add_header 指令不具备继承性。写在 http 块顶层的跨域头,不会自动传递到 server 或 location 块中——而浏览器最终收到的响应,是由具体处理该请求的 location 块生成的。如果那个 location 里没写 add_header,响应里就压根没有 CORS 头。
- 哪怕你在
http块写了十行add_header Access-Control-*,只要location /api/里没重复写,接口响应照样被浏览器拦截 - 静态资源(如
location ~ \.(js|css|png)$)同样需要单独加头,否则前端加载脚本时可能因资源本身跨域失败 - 宝塔等可视化面板的“添加响应头”功能,默认只作用于
location /,对/api/或/static/无效
OPTIONS 预检必须在 location 内显式拦截
浏览器对复杂请求(如带 Authorization、Content-Type: application/json 或非 GET/POST 方法)会先发 OPTIONS 请求。这个请求由 Nginx 内部路径处理,默认返回 204 但不带任何头——http 块的配置完全无法干预它。
- 必须在每个需要跨域的
location块内,用if ($request_method = 'OPTIONS') { ... return 204; }显式捕获 - 这个
if块里的add_header必须全部带always,否则 204 响应里依然没头 - 不能把预检逻辑放在
http或server级,Nginx 不允许在这些上下文中使用if+return处理请求方法
动态 Origin 匹配只能靠 map + location 组合
生产环境需允许多个域名(如正式站、测试站、本地开发),硬编码 Access-Control-Allow-Origin 不现实。这必须依赖 map 指令做变量映射,而 map 只能在 http 块定义,实际使用必须落在 location 里:
- 在
http块顶部写:map $http_origin $cors_origin { default ""; ~^https?://(localhost|127\.0\.0\.1):[0-9]+|$ your-domain\.com $http_origin; } - 但在
location中才写:add_header Access-Control-Allow-Origin $cors_origin always; - 若跳过
location直接想用$cors_origin,语法报错——变量只在声明它的上下文及其子块中有效
错误响应(4xx/5xx)也必须加 always
默认 add_header 只对 2xx/3xx 生效。当后端返回 401、500 或 Nginx 自身返回 502 时,若没加 always,响应里就没有 CORS 头,浏览器直接屏蔽错误详情,只报“跨域被拒绝”。
- 每个
add_header行都必须显式带上always,包括Allow-Origin、Allow-Methods、Allow-Headers等 -
always不可继承,不可省略,也不可通过一次设置全局开启 - 预检
if块内的所有add_header同样要加always,否则return 204的响应仍是空头
真正可行的做法是:在每个关键 location 块内,集中配置完整 CORS 头 + always + OPTIONS 拦截。http 块最多只放 map 定义和公共变量,不承担实际响应头注入职责。











