最可靠方式是用 parse_url($url, PHP_URL_HOST) 提取 host,再按需处理子域、IDN 和 IPv6;避免正则或 str_replace 硬切,注意反向代理下优先用 $_SERVER['SERVER_NAME']。

PHP中用 parse_url() 提取主域名最可靠
直接用 $_SERVER['HTTP_HOST'] 或 $_SERVER['SERVER_NAME'] 拿到的只是主机名(如 example.com 或 www.example.com),但如果你手头是一个完整 URL 字符串(比如 "https://blog.example.com:8080/path/to/page?x=1#section"),必须先解析再剥离路径。此时 parse_url() 是唯一推荐方式,它能准确分离 scheme、host、port、path、query、fragment。
常见错误是用 str_replace() 或正则硬切,结果在带端口、子域、IPv6 地址(如 [::1])或特殊字符时直接崩。
-
parse_url($url, PHP_URL_HOST)返回 host 部分,不含端口(如blog.example.com) - 若需保留端口,用
parse_url($url, PHP_URL_PORT)单独获取,再手动拼接 - 注意:
PHP_URL_HOST对 IPv6 地址会返回带方括号的格式([2001:db8::1]),这是合规行为,别误判为异常
去掉 www 前缀但不误伤其他子域?用条件判断别硬删
很多方案直接 str_replace('www.', '', $host),这会导致 www-admin.example.com 变成 admin.example.com,完全错误。正确做法是明确判断 host 是否以 www. 开头且长度刚好比 www. 多出一个合法域名长度(即后面至少含一个点 + 二级域)。
- 安全写法:
if (substr($host, 0, 4) === 'www.' && strpos($host, '.', 4) !== false) { $host = substr($host, 4); } - 更健壮可配合
filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)验证剥离后的合法性(PHP 7.4+) - 别用
preg_replace('/^www\./', '', $host)—— 没加锚定边界,可能误替换路径里的 www
从 $_SERVER 获取当前请求主域要注意协议和重写影响
如果目标只是当前页面的主域名(非任意 URL),不要自己拼 $_SERVER['HTTP_HOST'] 和 $_SERVER['HTTPS'],因为反向代理(如 Nginx + Apache)下 HTTP_HOST 可能被篡改,HTTPS 环境变量也可能缺失或值为 off。
立即学习“PHP免费学习笔记(深入)”;
- 优先用
$_SERVER['SERVER_NAME']—— 它来自 Web 服务器配置,更可信(但需确保 vhost 配置正确) - 若用 Nginx,务必设置
fastcgi_param SERVER_NAME $server_name;,否则 PHP 可能拿不到 - Apache 下开启
UseCanonicalName On并配好ServerName,才能让SERVER_NAME可靠
国际化域名(IDN)要转 Punycode 才能安全比较
遇到中文域名(如 例子.中国)时,parse_url() 返回的是 UTF-8 编码的 host,但 DNS 解析和 cookie 域名匹配要求 ASCII 格式(Punycode,如 xn--fsq.xn--fiqs8s)。直接拿原始 host 做域名白名单或存储,后续发 cookie 或跳转会失败。
- PHP 5.4+ 用
idn_to_ascii($host, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46)转为标准格式 - 转之前先用
idn_to_unicode()验证是否为合法 IDN,避免传入普通 ASCII 域名导致意外转换 - 注意:
INTL_IDNA_VARIANT_UTS46是当前标准,旧版IDNA_NONTRANSITIONAL_TO_ASCII已废弃
主域名提取看着简单,但真实环境里端口、IDN、代理头、子域策略、IPv6 这几处最容易漏掉验证。尤其别在没确认输入来源可信前,就对 host 做任何字符串操作。











