推荐使用map指令构建动态cors白名单:在http块中用正则匹配$http_origin生成$cors_origin变量,再在location中通过if判断其非空时添加带always标志的cors响应头,确保安全、灵活且可维护。

直接在 Nginx 中用 add_header Access-Control-Allow-Origin * 虽然简单,但不安全,也不满足“只允许多个特定域名”的要求。真正可靠的做法是借助 map 指令做白名单匹配,再结合条件判断动态注入响应头。
用 map 指令定义可信来源变量
这是最推荐、最清晰的方案。把所有允许的域名写进 map 块,Nginx 会根据请求头中的 Origin 自动匹配并赋值给一个变量(比如 $cors_origin):
放在 http 块内(全局生效,避免重复定义):
map $http_origin $cors_origin {
default "";
"~^https://app\.example\.com$" $http_origin;
"~^https://admin\.example\.com$" $http_origin;
"~^https://staging\.example\.com$" $http_origin;
"~^http://localhost:3000$" $http_origin;
}
注意:
• 正则以 ~^ 开头表示大小写敏感匹配,$ 表示精确结尾;
• 域名中的点号 . 需要转义为 \.;
• default "" 是兜底值,非白名单来源时变量为空,后续就不会加 CORS 头。
在 location 中有条件地添加响应头
只要 $cors_origin 不为空,就说明来源合法,此时才添加 CORS 相关头部:
- 确保只在 API 路径(如
/api/)下生效,避免静态资源也被加上跨域头 - 必须同时设置
Access-Control-Allow-Credentials true(如果前端带 cookie),且此时Access-Control-Allow-Origin不能为*,必须是具体域名 - 支持常见方法和请求头,例如
Content-Type、Authorization
示例配置:
location ^~ /api/ {
if ($cors_origin != "") {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Expose-Headers' 'Content-Length';
}
<pre class="brush:php;toolbar:false;"># 单独处理 OPTIONS 预检请求(推荐拆开,避免 if + proxy_pass 冲突)
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;}
避免常见错误
这些细节容易被忽略,但直接影响是否生效:
- 不要在同一个 location 里混用
if和proxy_pass—— Nginx 官方明确不推荐,可能导致 header 丢失或 502 错误 - 前后端不要同时配置 CORS —— 浏览器会拒绝含多个
Access-Control-Allow-Origin的响应 - 如果用了
Access-Control-Allow-Credentials true,Access-Control-Allow-Origin必须是具体域名,不能是* - 本地开发用
http://localhost:3000时,协议、端口、主机都要完全匹配,少一个字符都不行
验证是否生效
配置完成后重载 Nginx(sudo nginx -s reload),然后用 curl 或浏览器开发者工具检查响应头:
正常情况:当请求来自 https://app.example.com 时,响应中应有:Access-Control-Allow-Origin: https://app.example.comAccess-Control-Allow-Credentials: true
非法来源:比如 https://hacker.com 发起请求,响应中不应出现任何 Access-Control-Allow-* 头,浏览器自然拦截。











