<p>不能安全地用 Access-Control-Allow-Origin: 同时支持带凭证的请求,因浏览器规范禁止 Access-Control-Allow-Credentials: true 与通配符共存;公开接口可用 (不带 credentials),需凭证接口须动态白名单匹配可信源并设 Vary: Origin 等加固措施。</p>

不能安全地用 Access-Control-Allow-Origin: * 同时支持带凭证的请求。
这是硬性限制,不是配置技巧问题。浏览器规范明确禁止:只要响应头中包含 Access-Control-Allow-Credentials: true,Access-Control-Allow-Origin 就绝不能是 *,否则整个响应会被浏览器静默丢弃——前端收不到数据,也看不到错误提示,排查极其困难。
所以,“允许任何来源 + 带 Cookie/Authorization” 和 “用通配符 *” 这两件事根本无法共存。
真正可行的安全做法,是区分场景、按需配置:
*只对不带凭证的公开接口用 ``**
适用于:文档查询、天气 API、静态资源代理等无需用户身份上下文的场景。
- 确保前端请求没设
credentials: 'include'或withCredentials: true - Nginx 配置可简化为:
add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, HEAD' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,Content-Type' always;
对需要凭证的接口,必须用动态白名单
比如登录态校验、用户数据读写等场景,必须精确匹配可信源:
- 使用
map指令做运行时匹配,避免硬编码或重复判断 - 只有匹配成功的
$cors_origin才设置响应头,不匹配则不加头(浏览器自动拒绝) - 示例(放在
http块):map $http_origin $cors_origin { default ""; "~^https?://(app|admin|portal)\.example\.com$" "$http_origin"; "~^https?://localhost:3000$" "$http_origin"; }对应
location中:add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Credentials' 'true' always;
预检请求要单独处理OPTIONS 方法不能透传给后端,否则可能暴露内部逻辑或触发非幂等操作:
- 直接返回
204,并带上必要 CORS 头 - 不依赖后端响应,避免延迟和不确定性
额外加固点
-
Vary: Origin必须加上,防止 CDN 缓存污染 -
Access-Control-Expose-Headers只声明前端真正需要读取的响应头,避开Set-Cookie、Authorization等敏感字段 - 避免在
add_header中使用always修饰所有头(如Access-Control-Allow-Origin),因为if或map失败时不该发送该头
本质上,CORS 安全不靠“通配符是否够快”,而靠“声明是否准确”。把 * 当万能解,恰恰是多数线上 CORS 故障的根源。


















