Nginx 的 map 指令无法动态改写 upstream 块,因其仅支持静态定义;正确做法是用 map 映射请求来源生成后端地址变量,再通过 proxy_pass 动态转发,并在响应阶段统一注入匹配的 CORS 头。

直接用 map 指令动态改写 upstream 变量,无法真正“实现前后端跨域规则对齐”。因为 Nginx 的 map 是在请求进入阶段(rewrite 阶段前)做变量映射,而 upstream 块本身不接受运行时变量——它只支持静态定义或通过 set + proxy_pass 动态转发。所谓“对齐跨域规则”,本质是让后端服务的响应头(如 Access-Control-Allow-Origin)与前端请求来源一致,而这个逻辑应在后端控制,或由 Nginx 在响应阶段注入/重写响应头。
明确 map 的作用边界
map 适合做请求头、URI、Host 等输入信息的条件映射,生成新变量供后续指令使用。但它不能改变 upstream 块结构,也不能让一个 upstream 块“动态绑定”多个后端地址。常见误用是试图这样写:
upstream backend {
server $dynamic_upstream_addr; # ❌ 不允许变量出现在 upstream 块中
}正确做法是:用 map 提前判断来源,生成目标后端地址变量,再在 location 中用 proxy_pass http://$backend_addr 转发。
用 map + proxy_pass 实现按来源路由
这是最常用且合规的方式:根据 $http_origin 或 $host 映射到不同后端地址,间接支撑跨域策略差异化。
- 定义映射关系(支持正则和通配):
map $http_origin $backend_addr {
default "127.0.0.1:8001";
"~^https?://(dev|staging)\.example\.com$" "127.0.0.1:8002";
"~^https?://prod\.example\.com$" "127.0.0.1:8003";
}- 在 location 中调用:
location /api/ {
proxy_pass http://$backend_addr;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}这样,不同来源的请求被分发到不同后端实例,各实例可独立配置 CORS 响应头,实现“规则对齐”。
跨域响应头由 Nginx 统一注入
如果后端无法修改响应头,可在 Nginx 层统一添加,确保与请求来源匹配:
- 允许指定 Origin(推荐,更安全):
if ($http_origin ~* "^https?://(dev|staging|prod)\.example\.com$") {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' "true" always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;
}- 对预检请求(OPTIONS)快速响应:
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' "true" always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;
add_header 'Access-Control-Max-Age' 17280000 always;
add_header 'Content-Type' 'text/plain; charset=utf-8' always;
add_header 'Content-Length' 0 always;
return 204;
}避免常见陷阱
-
不要在 map 中依赖尚未解析的变量:比如
$request_uri可用,但$args在 map 执行时可能未完全解析,建议用$query_string更可靠; -
Origin 为空时(如本地 file:// 页面)需单独处理:Nginx 中
$http_origin为空字符串,map 默认值会生效,但浏览器会拒绝空 Origin 的响应,建议前端避免此类请求; - add_header 在 if 中需加 always:否则仅对 2xx/3xx 响应生效,OPTIONS 返回 204 时会漏掉 CORS 头;
-
proxy_pass 后不要加 URI 路径(除非有意重写):如
proxy_pass http://$addr/会剥离 location 匹配路径,容易导致后端路由错乱。


















