php中匹配泛域名需先用正则/^\*\.[a-za-z0-9]([a-za-z0-9\-]{0,61}[a-za-z0-9])?(\.[a-za-z]{2,})+$/验证格式,再通过分段比对尾部域名实现精准模式匹配,并配合filter_var校验安全性。

PHP中用preg_match匹配泛域名的正则写法
泛域名(wildcard domain)本质是通配符匹配,不是DNS解析功能,PHP里只能靠正则判断是否符合 *.example.com 这类模式。关键不是“获取”,而是“识别”——先确认输入字符串是否为合法泛域名格式。
常见错误是直接用 parse_url() 或 gethostbyname() 处理,它们根本不支持 *,会返回空或报错。
- 正确正则:
/^\*\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$/ -
*必须在开头且紧跟.,不能写成*.com或**.example.com - 二级泛域名如
*.sub.example.com也合法,但需确保每段符合域名规则(不能以连字符开头/结尾) - 注意:该正则不校验TLD是否存在,仅做格式验证;真实业务中建议额外查
dns_get_record()确认主域名可解析
从HTTP Host头提取并判断是否为泛域名匹配
实际场景中,你往往收到一个请求的 Host 值(如 api.example.com),需要判断它是否落在某个泛域名规则(如 *.example.com)下。这不是正则匹配字符串,而是“模式匹配目标”。
别用 str_replace('*', $host, $pattern) 这种粗暴方式——*.example.com 匹配 foo.bar.example.com 是非法的,但那样会误判。
立即学习“PHP免费学习笔记(深入)”;
- 先分割泛域名模板:
$parts = explode('.', ltrim($wildcard, '*.'));得到['example', 'com'] - 再分割实际host:
$hostParts = explode('.', $host); - 检查尾部是否一致:
array_slice($hostParts, -count($parts)) === $parts - 必须保证
$hostParts长度 >$parts长度,否则example.com会被误认为匹配*.example.com
用filter_var校验域名后再做泛匹配
很多人跳过基础校验,直接上正则,结果把 *.exa_mple.com 或 *.example. 当成合法输入。PHP原生有更稳的方式:
- 先用
filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)校验原始host是否为有效域名 - 对泛域名模板,先去掉
*和点,再用同样方式校验剩余部分(即example.com) - 如果校验失败,直接拒绝,不进入后续匹配逻辑——避免正则绕过导致的逻辑漏洞
- 注意:
FILTER_VALIDATE_DOMAIN在 PHP 5.6+ 才完整支持,旧版本需降级用idn_to_ascii()+ 正则兜底
$_SERVER['HTTP_HOST']可能被伪造,泛域名匹配不能用于安全决策
泛域名匹配常被用在多租户路由、子站分发等场景,但有个硬伤:$_SERVER['HTTP_HOST'] 完全由客户端控制,可轻易伪造。哪怕你严格匹配了 *.myapp.com,攻击者仍可发请求带 Host: *.myapp.com(注意:这本身是非法host值,但某些Web服务器会透传)。
- 真正可信的只有 TLS SNI(服务端能拿到)和反向代理设置的
X-Forwarded-Host(需白名单校验) - 若用Nginx,应在配置中用
map提前过滤非法host,PHP层只处理已清洗过的变量 - 泛域名规则本身不应存于数据库或用户可控配置中;若必须动态加载,请对模板做双重校验(格式 + 是否属于白名单根域)
泛域名匹配看着简单,但跨协议(HTTP/HTTPS)、跨部署(直连/CDN/反代)、跨PHP版本的兼容细节很多,最容易忽略的是——你写的匹配逻辑,到底是在校验输入,还是在替代DNS解析。











