Nginx动态反射CORS需白名单校验,不可直接返回$http_origin;应通过map定义可信域名,仅对匹配源设置Access-Control-Allow-Origin,并配合Credentials、OPTIONS预检等安全配置。

在 Nginx 中实现跨域(CORS)时,直接写死 Access-Control-Allow-Origin 只能支持单个域名,而用 $http_origin 动态反射来源域名,是安全支持多域名跨域的关键方式——但必须配合严格校验,否则会引发严重安全风险。
为什么不能直接返回 $http_origin?
浏览器允许 Access-Control-Allow-Origin 的值为 * 或**精确匹配的单个源**(协议+域名+端口)。若无条件地写成:
add_header Access-Control-Allow-Origin $http_origin;
——当请求头中 Origin: https://evil.com 时,Nginx 也会返回 https://evil.com,相当于向任意站点开放跨域权限,等同于裸奔。
安全做法:白名单匹配 + 条件反射
只对可信域名列表中的来源,才反射其 Origin;其他请求不设置该头(或设为 *,但需同时禁用凭证)。推荐写法:
```nginx
# 定义可信域名白名单(支持正则或 map)
map $http_origin $cors_origin {
default ""; # 默认不匹配,留空
"~^https?://(localhost|myapp\.com|api\.trusted\.site)(:[0-9]+)?$" "$http_origin";
"~^https://sub\.(a|b)\.example\.org$" "$http_origin";
}
server {
location /api/ {
if ($cors_origin != "") {
add_header Access-Control-Allow-Origin $cors_origin;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
add_header Access-Control-Expose-Headers "Content-Length,Content-Range";
add_header Access-Control-Allow-Credentials "true";
}
# 处理预检请求
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;
}
}
关键细节与避坑提醒
-
带凭证(credentials)时,
Access-Control-Allow-Origin绝不能为*:若前端设置了credentials: 'include',后端必须返回具体域名,且 Nginx 不能漏掉Access-Control-Allow-Credentials "true" -
预检请求(OPTIONS)必须被拦截并快速响应:不要让它透传到后端应用,否则可能 404 或超时;用
return 204是最轻量可靠的方式 -
白名单建议用
map而非多个if:Nginx 的if在 location 中有局限性,map更高效、更清晰,且支持正则和变量复用 -
注意端口和协议一致性:
http://localhost:3000和https://localhost是不同源,白名单正则中建议显式覆盖常见开发端口(如:3000|:8080)
调试与验证技巧
部署后,用 curl 模拟跨域请求快速验证:
```bash
curl -H "Origin: https://myapp.com" \
-H "Access-Control-Request-Method: POST" \
-X OPTIONS -I https://your-api.com/api/data
```
检查响应头是否含 Access-Control-Allow-Origin: https://myapp.com,且无多余头泄露。也可在浏览器开发者工具 Network 标签中查看真实请求的响应头。

















