nginx实现安全跨域需动态匹配origin并返回具体域名:用if正则或map指令设置$allowed_origin,add_header配合always确保cors头生效;必须拦截options请求返回204并带全cors头;携带cookie时access-control-allow-origin不可为*且需设access-control-allow-credentials为true。

在 Nginx 中只允许指定的跨域来源(比如 https://a.example.com 和 https://b.example.com),关键在于**动态判断请求头中的 Origin,匹配后才返回合法的 Access-Control-Allow-Origin 值**,而不是直接写死 *。这样既满足安全要求,又避免暴露给任意域名。
用 if + 正则匹配指定域名
这是最常用、兼容性好、无需额外模块的方式。把以下配置放在 location 块内(或 server 块中):
- 先用
if判断$http_origin是否匹配预设的正则表达式 - 匹配成功则设置变量(如
$cors_origin)为该 Origin 值 - 再用
add_header ... $cors_origin always输出响应头 -
always参数很重要——确保即使返回 304 或错误码时 header 也生效
示例配置:
set $cors_origin "";
if ($http_origin ~* "^https?://(a\.example\.com|b\.example\.com)(:[0-9]+)?$") {
set $cors_origin $http_origin;
}
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;用 map 指令实现更清晰的多域名管理
如果允许的域名较多,或希望逻辑更解耦、性能略优(map 在配置加载时预编译),推荐用 map 块定义映射关系,放在 http 级别:
-
map根据$http_origin的值,将匹配结果赋给新变量(如$allowed_origin) - 未匹配时变量为空,Nginx 不会输出该 header,规避安全风险
- 支持精确匹配、前缀、正则,语法简洁可读性强
示例(http 块中):
map $http_origin $allowed_origin {
default "";
"~^https?://a\.example\.com(:[0-9]+)?$" "$http_origin";
"~^https?://b\.example\.com(:[0-9]+)?$" "$http_origin";
"~^https?://admin\.myapp\.io(:[0-9]+)?$" "$http_origin";
}然后在 location 中使用:
add_header 'Access-Control-Allow-Origin' $allowed_origin always; add_header 'Access-Control-Allow-Credentials' 'true' always; # 其他 CORS 头保持不变
必须处理 OPTIONS 预检请求
当请求含自定义 header(如 Authorization)或非简单方法(如 PUT、DELETE)时,浏览器会先发一个 OPTIONS 请求探路。Nginx 必须正确响应它,否则请求直接失败:
- 检查
$request_method是否等于OPTIONS - 返回
204 No Content,并带上必要的 CORS 头(如Allow-Methods、Max-Age) - 注意:不能让
proxy_pass拦截 OPTIONS 请求,否则后端可能不识别
补充配置:
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000 always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
return 204;
}携带 Cookie 时的注意事项
如果前端设置了 credentials: 'include'(比如要传 session cookie),则必须同时满足:
-
Access-Control-Allow-Origin不能是*,必须是具体域名(上面两种方式天然支持) -
Access-Control-Allow-Credentials设为true - 前端 fetch 请求中
credentials字段需显式声明(axios 默认不带,需配withCredentials: true)











