Access-Control-Allow-Origin: * 在带凭证请求时失败,因浏览器禁止凭证请求使用通配符,后端须返回具体域名;Nginx需用map白名单校验Origin,Express应使用cors包显式配置白名单,且必须正确处理OPTIONS预检请求。
为什么 Access-Control-Allow-Origin: * 在带凭证请求时会失败
因为浏览器明确禁止在 credentials(如 cookie、authorization 头)存在时,把 access-control-allow-origin 设为通配符 *。这是硬性安全限制,不是服务器配置没生效。
- 只要前端设置了
fetch({ credentials: 'include' })或XMLHttpRequest.withCredentials = true,后端就必须返回一个**具体域名**,比如https://app.example.com - 如果后端动态读取请求头中的
Origin并原样回写,要注意校验——不能无条件反射,否则会放大攻击面(例如允许恶意站点伪造 Origin) - Nginx 里用
add_header Access-Control-Allow-Origin $http_origin;是危险的,必须配合map白名单过滤
如何在 Nginx 中安全地支持多域名跨域
靠正则或字符串匹配做白名单,而不是放行所有来源。关键是把可信域名列清楚,拒绝未声明的 Origin。
- 用
map指令预定义合法来源:map $http_origin $cors_origin { default ""; "~^https?://(app\.example\.com|dashboard\.example\.org)$" $http_origin; } - 在 server 块中启用 CORS 响应头:
add_header 'Access-Control-Allow-Origin' $cors_origin; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
- 务必拦截非法
Origin:当$cors_origin为空时,不发送任何 CORS 头,让浏览器自然报错
Express 中设置 Access-Control-Allow-Origin 的常见误操作
直接用中间件无条件写死或反射 req.headers.origin,等于放弃控制权。
- 别用
res.header('Access-Control-Allow-Origin', req.headers.origin)—— 这等于把 Origin 校验交给前端,毫无意义 - 推荐用
cors包并显式传入白名单:app.use(cors({ origin: ['https://app.example.com', 'https://dashboard.example.org'], credentials: true })); - 如果需要动态判断(比如按用户权限开放不同域名),必须自己实现逻辑,且确保 fallback 行为是「不发 CORS 头」而非「发
*」
为什么 OPTIONS 预检请求总返回 404 或 500
因为很多后端框架默认不处理 OPTIONS 方法,或者路由中间件没覆盖到预检路径。
- 确保路由能响应
OPTIONS:Express 中可在app.options('*', cors(...));Nginx 中可统一用if ($request_method = 'OPTIONS') { add_header ...; return 204; } - 检查是否被其他中间件拦截:比如身份验证中间件在 CORS 之前执行,对
OPTIONS请求也尝试校验 token,导致失败 - 注意路径匹配粒度:若只对
/api/开启 CORS,但前端请求的是/api/v1/users,而 OPTIONS 被路由规则漏掉,就会 404
file:// 协议测试、以及把测试域名写进白名单却忘了上线清理这些地方。











