不安全——直接用用户输入或未校验数组值设 access-control-allow-origin 等于开放任意来源;必须用完整 url 格式白名单,严格匹配 scheme、host(及 port),禁用模糊匹配。

PHP 中用数组动态设置 Access-Control-Allow-Origin 安全吗?
不安全 —— 直接把用户传入的域名或未校验的数组值塞进响应头,等于开放任意来源,Access-Control-Allow-Origin: * 都比它可控。
真正能用的方案必须满足两个前提:域名白名单 + 严格匹配。浏览器只认完全一致的字符串,不支持通配符(除了 *),也不接受逗号分隔多个值。
- 必须先从可信配置中取出预设域名数组,比如
$allowed_origins = ['https://a.com', 'https://b.net'];</li> <li>再用 <code>$_SERVER['HTTP_ORIGIN']
获取请求头中的原始来源,不能用$_SERVER['HTTP_REFERER']替代 - 逐个比对是否完全相等,匹配成功才写入响应头,不匹配就跳过或返回 403
- 绝对不要用
strpos()或stripos()做模糊匹配,https://evil.com.evil-attacker.com可能被误放行
怎么写一个防绕过的域名白名单匹配逻辑?
关键不是“能不能写”,而是“怎么避免漏掉协议、端口、斜杠这些细节”。常见错误是只比对主机名,结果 http://api.example.com 和 https://api.example.com:8080 被当成同一个源。
- 用
parse_url()解析$_SERVER['HTTP_ORIGIN'],提取scheme、host、port三部分 - 白名单里的每个域名也得是完整 URL 格式,比如
'https://api.example.com',而不是'api.example.com' - 匹配时必须同时校验
scheme和host;port如果没显式写出(如 80/443),则忽略比较 - 结尾不加
/,避免https://a.com/和https://a.com被判为不同
if (isset($_SERVER['HTTP_ORIGIN'])) {
$origin = $_SERVER['HTTP_ORIGIN'];
$parsed = parse_url($origin);
if ($parsed && isset($parsed['scheme'], $parsed['host'])) {
foreach ($allowed_origins as $allowed) {
$a = parse_url($allowed);
if ($a && $a['scheme'] === $parsed['scheme'] && $a['host'] === $parsed['host']) {
header('Access-Control-Allow-Origin: ' . $origin);
break;
}
}
}
}
Access-Control-Allow-Credentials: true 和数组白名单冲突吗?
冲突 —— 一旦开启凭据支持,Access-Control-Allow-Origin 就不能再是 *,且必须精确匹配单个来源。这意味着你不能在白名单里写多个域名然后动态选一个返回,而必须确保每次响应只返回一个确定的、匹配成功的值。
立即学习“PHP免费学习笔记(深入)”;
- 如果业务需要带 cookie 登录态跨域,就必须放弃“多域名共用一套后端”的偷懒做法
- 前端发起请求时,
fetch或XMLHttpRequest必须显式设置credentials: 'include' - 后端匹配失败时,不能 fallback 到其他域名,也不能返回空头;该拒绝就拒绝,否则浏览器直接报错
Response to preflight request doesn't pass access control check - 测试时注意:本地
file://协议、localhost和127.0.0.1是不同源,别漏掉开发环境域名
为什么 Nginx / Apache 层做跨域比 PHP 更稳?
因为 PHP 是应用层,出错或异常退出时可能根本没走到 CORS 头设置逻辑;而 Web 服务器只要配置生效,就稳定输出,不依赖脚本执行流程。
- Nginx 可用
map指令做域名映射:map $http_origin $cors_origin { ... },再在location里用add_header Access-Control-Allow-Origin $cors_origin - Apache 用
SetEnvIfNoCase Origin+Header set Access-Control-Allow-Origin组合,同样支持正则和条件判断 - 但注意:Web 服务器配置无法处理需要运行时逻辑的场景,比如按用户角色动态放行域名、或读取数据库白名单 —— 这类需求仍得回 PHP 层做
- 混合方案最常用:Nginx 兜底基础白名单,PHP 补充动态规则(如 SaaS 多租户子域名)
Access-Control-Allow-Origin 必须和后续真实请求的值完全一样,否则浏览器直接拦截,连 PHP 脚本都不会执行。











