不能通过http块的add_header实现真正有效的跨域全局策略,因其不生效且无继承性,必须在每个location块中显式配置cors头、options预检及always标记。

不能通过 http 块的 add_header 实现真正有效的跨域全局策略。
add_header 在 http 块中不生效
add_header 指令不具备继承性。写在 http 块顶层的 CORS 头,不会自动传递到 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 指令做变量映射:
- 在
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,语法报错——变量只在声明它的上下文及其子块中有效
错误响应也必须加 always
默认 add_header 只对 2xx/3xx 生效。当后端返回 401、500 或 Nginx 自身返回 502 时,若没加 always,响应里就没有 CORS 头,浏览器直接屏蔽错误详情,只报“跨域被拒绝”。
- 所有关键 CORS 头都应带
always:包括Access-Control-Allow-Origin、-Methods、-Headers、-Credentials等 - 预检响应(
return 204)中还需额外补上Content-Length 0和Content-Type text/plain; charset=utf-8 - 确保
Vary: Origin被设置(可手动加add_header Vary Origin always;),避免缓存污染











