Nginx需避免在credentials请求中使用Access-Control-Allow-Origin: *,必须指定确切域名;通过map指令动态匹配白名单域名,并在location中条件化设置CORS响应头以兼顾安全与灵活性。

要让Nginx既支持通配符(*)的便捷性,又不破坏安全策略对特定域名的精确控制,关键在于**避免在含凭证(credentials)请求时使用通配符**,并用条件判断区分可信与非可信来源。
为什么不能直接用 Access-Control-Allow-Origin: * 配合 credentials
当浏览器请求携带 withCredentials = true(如带 Cookie 或 Authorization 头)时,Nginx 若设置 Access-Control-Allow-Origin: *,浏览器会直接拒绝响应——这是 CORS 规范的硬性限制。此时必须指定**确切的单个域名**,不能是通配符或逗号分隔列表。
用 map 指令实现域名白名单动态匹配
通过 Nginx 的 map 模块,可将请求头中的 Origin 映射为预设的允许值,兼顾灵活性与安全性:
# 在 http 块中定义映射关系
map $http_origin $cors_origin {
default "";
"~^https?://(localhost|dev\.example\.com|app\.mycompany\.io)(:[0-9]+)?$" "$http_origin";
"~^https?://([a-z0-9\-]+\.)?production\-site\.com$" "$http_origin";
}
说明:
-
default ""表示不匹配任何白名单时,$cors_origin为空,后续可跳过 CORS 头输出 - 正则支持子域名通配(如
?production-site.com匹配www.production-site.com和api.production-site.com) - 不建议在正则中写死端口,除非开发环境明确需要(如
localhost:3000)
在 server 或 location 块中注入 CORS 响应头
利用上一步的 $cors_origin 变量,精准输出响应头:
location /api/ {
# 允许携带凭证
add_header 'Access-Control-Allow-Credentials' 'true';
<pre class='brush:php;toolbar:false;'># 动态设置 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-Max-Age' 1728000;
# 处理预检请求直接返回 204
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;
}}
注意:
- 不要在
if块内重复写add_header,Nginx 中if内的add_header会覆盖外层同名头 - 若无需支持 credentials,且希望简单兼容所有前端调试域名,可单独配置一个不带
credentials的 fallback 规则(例如对非白名单 Origin 返回*,但移除Allow-Credentials)
补充:调试与验证技巧
上线前务必验证行为是否符合预期:
- 用
curl -H "Origin: https://malicious.com" -I https://yoursite.com/api/test检查是否**不返回**Access-Control-Allow-Origin头 - 用
curl -H "Origin: https://app.mycompany.io" -H "Cookie: sid=abc" -I https://yoursite.com/api/test确认头中Access-Control-Allow-Origin值与请求 Origin 完全一致,且包含Access-Control-Allow-Credentials: true - 浏览器开发者工具 Network 标签页中查看响应头,注意预检(OPTIONS)和实际请求(GET/POST)的头是否都正确


















