PHP取域名最标准的方法是使用$_SERVER['HTTP_HOST']配合parse_url解析$_SERVER['REQUEST_URI'],但真正严谨的做法是先判断$_SERVER['SERVER_NAME'],再结合HTTPS状态拼接完整URL。
很多开发者写PHP程序时,第一步就是获取当前访问的域名,用来生成绝对链接、做跨域判断、或者调用第三方API,但网上搜到的代码五花八门,有的用了$_SERVER['SERVER_NAME'],有的用$_SERVER['HTTP_HOST'],还有的直接拿$_SERVER['REQUEST_URI']切来切去,到底哪个靠谱?这篇文章不讲虚的,直接给你能落地的代码和场景拆解。
PHP获取域名的基础方法:三种内置变量对比
PHP里能碰到的域名相关变量基本就是$_SERVER里的那几个键,搞懂它们的区别,比背一百行代码都强,下面这张表是核心。
| 变量名 | 典型值 | 风险点 | |
|---|---|---|---|
$_SERVER['HTTP_HOST'] |
请求头里的Host字段,包含端口号 | www.example.com:8080 |
可被HTTP头伪造,但大多数现代框架仍然用它 |
$_SERVER['SERVER_NAME'] |
服务器配置中的服务器名 | www.example.com |
受Apache/Nginx的ServerName配置影响,一般不含端口 |
$_SERVER['SERVER_ADDR'] |
服务器IP地址 | 168.1.1 |
不是域名 |
取纯域名(不带端口)的标准做法:
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
$domain = preg_replace('/:d+$/', '', $host); // 去掉端口
这个写法兼容了HTTP_HOST缺失的情况,同时用正则把端口号剥离,注意是PHP 7+的空合并运算符,老项目改成isset()判断。
为什么不能用$_SERVER['REQUEST_URI']直接取域名?
REQUEST_URI里存的是路径和查询字符串,比如/index.php?id=1,它压根不包含域名,但很多老教程会教你先拼$_SERVER['HTTP_HOST']再拼REQUEST_URI,这就是拿两个变量硬凑,真正需要的是HTTP_HOST和HTTPS状态组合成的完整URL。
完整URL拼法:
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $fullUrl = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
这样拿到的$fullUrl就是类似https://www.example.com/path?query=1的完整地址,注意HTTPS的判断不能只检查非空,因为有些服务器下HTTPS的值可能是off。
端口带不带?什么时候必须带上?
本地开发经常用localhost:8080或0.0.1:8000,此时

HTTP_HOST的值就是带端口的,如果你只是取域名用来展示,去掉端口更干净,但如果是生成API回调地址,端口必须原样保留,否则回调就跑到80端口去了。
保留端口的写法:
$domainWithPort = $_SERVER['HTTP_HOST'];
去掉端口的写法:
$domain = strtok($_SERVER['HTTP_HOST'], ':');
strtok比正则速度更快,一行解决。
PHP取子域名并替换顶级域名:实际业务场景怎么写?
很多站点有PC端和手机端的域名区分,比如www.example.com和m.example.com,用户访问手机版时要跳转到对应子域名,这就涉及到从完整域名中提取子域名、甚至替换域名主体。
提取子域名:
$host = $_SERVER['HTTP_HOST'];
// 去掉端口
$host = strtok($host, ':');
// 将域名拆分为数组
$parts = explode('.', $host);
// 如果数组长度大于2,则第一个元素是子域名(不考虑多级子域名情况)
$subdomain = (count($parts) > 2) ? $parts[0] : null;
比如m.example.com会拆成['m', 'example', 'com'],子域名就是m,但你要处理www.example.co.uk这类多段后缀,上面的逻辑就会出错,稳妥的办法是用一个公共后缀列表(Public Suffix List)来判断,但那种做法对普通网站来说太重了,多数情况下我们只需要处理常见的.com、.cn、.net。
替换顶级域名:假设你要把www.example.com改成www.example.net,不能直接str_replace('.com', '.net'),万一域名里其他地方也有.com字符串呢?正确做法是只替换最后一段:
$host = 'www.example.com'; $pos = strrpos($host, '.'); // 找到最后一个点 $newHost = substr($host, 0, $pos) . '.net'; // 前缀保持不变
这种写法对www.example.com.cn这样的域名同样有效,因为它替换的是最后的.cn段。
处理主域名与二级域名的逻辑
取主域名(不带www和子域名)是PHP中常见的需求,尤其是做Cookie跨域共享或单点登录时,网上流传的很多正则表达式看着很吓人,其实核心就两步:
- 省略
www - 去掉端口
function getMainDomain($host) {
$host = strtolower($host);
$host = preg_replace('/:d+$/', '', $host);
// 移除 www 前缀(如果有)
if (substr($host, 0, 4) === 'www.') {
$host = substr($host, 4);
}
return $host;
}
注意这个函数不处理m.example.com,它会返回m.example.com,如果你想把m也去掉,就需要额外判断,但主域名到底是指根域名还是二级域名,取决于你的业务定义

,有的网站把.com.cn视为有效宿主,有的则把example.com.cn视为根域名,没有统一标准。
通过URL函数反向解析域名:parse_url的妙用
parse_url()是PHP内置的URL解析函数,它可以把一个完整URL拆成数组,如果你手里已经有一个URL字符串,想拿到里面的域名,用它是最稳的。
$url = 'https://user:pass@www.example.com:8080/path?arg=1#fragment'; $parts = parse_url($url); $domain = $parts['host']; // 输出 www.example.com
parse_url返回的数组里host是不带端口和协议的,但注意它对中文域名和特殊字符可能处理不友好,遇到https://例子.测试这样的国际化域名,建议先idn_to_ascii()转换再解析。
从referer里取来源域名
做流量统计时经常要解析用户是从哪个网站跳转过来的,此时先拿$_SERVER['HTTP_REFERER'],再用parse_url取host:
$referer = $_SERVER['HTTP_REFERER'] ?? ''; $refererDomain = $referer ? parse_url($referer, PHP_URL_HOST) : '';
这种写法适用于统计外链来源,但提醒一句,HTTP_REFERER是浏览器提供的,可以被伪装,千万别拿它当安全判断依据。
兼容HTTPS与多域名的完整封装函数
把前面的知识串起来,封装成一个函数,这个函数满足日常95%的需求:返回当前请求的协议、域名、端口和完整URL。
function getCurrentUrlInfo() {
// 协议
$https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
$scheme = $https ? 'https' : 'http';
// 主机(含端口)
$hostWithPort = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
// 分离主机和端口
$host = strtok($hostWithPort, ':');
$port = strtok(':') ?? ($https ? '443' : '80');
return [
'scheme' => $scheme,
'host' => $host,
'port' => $port,
'host_with_port' => $hostWithPort,
'full_url' => $scheme . '://' . $hostWithPort . ($_SERVER['REQUEST_URI'] ?? ''),
];
}
怎么用?比如你在做API签名校验,需要判断回调域名是否在白名单里:
$info = getCurrentUrlInfo();
$allowedDomains = ['www.example.com', 'api.example.com'];
if (!in_array($info['host'], $allowedDomains)) {
// 拒绝访问
}
PHP取域名时常见的5个坑
先说结论:没有完美的全局变量,只有适合场景的取舍。
HTTP_HOST受客户端伪造影响,但它代表用户实际请求的域名,如果你要根据不同域名输出不同内容,用它是准确的。SERVER_NAME由服务器配置决定,相对稳定,但可能和用户实际请求不一致(比如配置了多个虚拟主机)。- 使用了CDN或反向代理时,
$_SERVER['HTTP_HOST']可能是源站域名,也可能被回源请求覆盖,此时通常需要读取X-Forwarded-Host头,但要谨慎使用,因为在某些配置下该头会被客户端伪造。 - 命令行CLI模式下所有
$_SERVER相关变量都不存在,测代码时别用php cli.php直接测,得用内置服务器或配个虚拟主机。 - 如果站点部署在负载均衡后面,
$_SERVER['HTTPS']可能永远为空,因为外部SSL在LB层就结束了,此时需要看$_SERVER['HTTP_X_FORWARDED_PROTO']是否为https。

PHP取域名代码的实战测试:用内置服务器模拟
不用上服务器,本地就能验证代码,打开终端进入项目目录,执行:
php -S localhost:8080
然后写个test.php:
<?php echo $_SERVER['HTTP_HOST']; // 输出 localhost:8080 echo PHP_EOL; echo strtok($_SERVER['HTTP_HOST'], ':'); // 输出 localhost
还可以用curl -H "Host: test.example.com" http://localhost:8080/test.php来模拟Host头注入,你会发现这时HTTP_HOST变成了test.example.com,但SERVER_NAME还是localhost,这就是两者区别的最直观演示。
PHP取域名最佳实践总结
多数情况下我推荐用HTTP_HOST作为主数据源,SERVER_NAME作为兜底,取纯域名时一定要去掉端口,如果是生成链接用来重定向,保留完整Host即可,记住一点:域名获取没有银弹,取决于你是在处理HTTP业务,还是在处理服务器配置。
常见问题:PHP取域名相关问答
为什么$_SERVER['HTTP_HOST']有时候拿不到值?
在命令行模式下运行PHP脚本时,没有HTTP请求,自然不会有HTTP_HOST,另外某些PHP配置(如variables_order不包含S)也会导致$_SERVER为空,脚本最好加上来容错。
$_SERVER['SERVER_NAME']和HTTP_HOST选哪一个?
如果你做的是前台展示业务,选HTTP_HOST,因为它反映用户输入的域名,如果是后台判断服务器身份,选SERVER_NAME,业内专家指出,多数PHP框架(如Laravel、ThinkPHP)在生成URL时默认优先使用HTTP_HOST,这已经形成了行业共识。
如何用PHP获取当前页面的三级域名?
先把Host取出来去掉端口,然后按点拆分成数组,如果数组元素个数大于3,那么$parts[0]就是三级域名,比如a.b.example.com拆完是['a','b','example','com'],$parts[0]是a,$parts[1]是b,注意国内部分地区的运营商可能给网站加跳转页,实际生产环境取到三级域名的概率很低,别在这个功能上过度设计。
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/776108.html

