在PHP中获取当前域名,最直接的方法是使用$_SERVER['HTTP_HOST'],但在安全性和兼容性上,$_SERVER['SERVER_NAME']结合parse_url()处理才是更严谨的组合。无论你是想获取完整URL、剥离端口号,还是提取根域名,本文整理了不同场景下的全部实操方案。
php获取当前域名的基础方法
对于日常开发来说,php获取当前域名最常见的方式就是调用$_SERVER超全局变量,以下几个内置变量你需要分清用途,因为它们在特定环境下返回的值会有差异。
使用$_SERVER[‘HTTP_HOST’]获取域名
HTTP_HOST 是HTTP请求头中携带的Host字段,它包含了域名和端口号。
<?php // 输出类似:www.example.com 或 www.example.com:8080 $host = $_SERVER['HTTP_HOST']; echo $host; ?>
之所以说它是”最直接”的方法,是因为它请求发什么就返回什么,不经过服务器端的解析过程,实际开发中,绝大多数框架(如Laravel、ThinkPHP)在获取当前域名时,底层用的都是这个变量。
使用$_SERVER[‘SERVER_NAME’]获取域名
SERVER_NAME 是服务器配置中定义的名称,它通常定义在Apache或Nginx的ServerName指令中。
<?php // 输出类似:www.example.com $serverName = $_SERVER['SERVER_NAME']; echo $serverName; ?>
HTTP_HOST信任用户请求,SERVER_NAME信任服务器配置,两者在正常环境下并无二致,但若是服务器IP直连、命令行执行(CLI)或者URL重写规则不当时,这两个值就可能出现差异。
两者的区别与实际选择
在实际项目中,我们会根据场景来选择:
- 需要兼容CDN或负载均衡时的服务器内部跳转,使用
SERVER_NAME更稳定 - 用于前端资源路径拼接(如生成图片URL),使用
HTTP_HOST更符合用户访问的实际地址 - 做域名合法性校验时,强烈建议优先使用
SERVER_NAME,避免Host头攻击
php获取完整URL的实践方案
php获取完整url 是另一个高频搜索需求,所谓完整URL,就是包含协议(http/https)、域名、端口和请求路径的整串地址。
获取协议头(判断HTTP还是HTTPS)
要拼出完整URL,首先要判断当前请求走的是HTTP还是HTTPS协议,这里需要同时判断三个条件:
<?php
function getProtocol() {
if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
return 'https://';
}
if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
return 'https://';
}
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
// 处理Nginx反代或负载均衡场景
return 'https://';
}
return 'http://';
}
?>

判断HTTPS不能只看$_SERVER['HTTPS'],相当一部分项目部署在Nginx反向代理后面,HTTPS在负载均衡层已终结,行业共识认为,应该先检查HTTP_X_FORWARDED_PROTO,再判断SERVER_PORT,最后回退到HTTPS。
拼接完整URL的代码实现
结合HTTP_HOST、REQUEST_URI和协议头,就能生成当前页面的完整URL:
<?php
function getFullUrl() {
$protocol = getProtocol();
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
return $protocol . $host . $uri;
}
// 输出:https://www.example.com/index.php?id=123
echo getFullUrl();
?>
这里有一个细节:REQUEST_URI包含了路径和查询字符串,如果只想获取路径部分就用$_SERVER['PHP_SELF'],只用查询参数就用$_SERVER['QUERY_STRING']。
获取带端口的域名
如果你的本地环境或某些测试环境访问的URL带端口号,比如http://localhost:8080,HTTP_HOST会直接返回localhost:8080,如果你需要将域名和端口拆开使用:
<?php
$host = $_SERVER['HTTP_HOST']; // localhost:8080
$parsed = parse_url('http://' . $host);
echo $parsed['host']; // localhost
echo $parsed['port']; // 8080
?>
用parse_url伪装一个协议头来解析,是目前处理”域名+端口”最轻量的方案,无需写正则。
php获取根域名与主域名的处理技巧
在Cookie跨域共享、第三方登录的回调地址比对、统计系统中,我们往往需要php获取根域名(即忽略www前缀后的主域名部分),比如sub.example.com和www.example.com的根域名都是example.com。
用PHP数组操作提取主域名
对于常见场景(二三级域名),用explode切分即可:
<?php
function getRootDomain($host) {
// 移除端口号
$host = preg_replace('/:d+$/', '', $host);
$parts = explode('.', $host);
$count = count($parts);
// 处理常见后缀:com.cn, net.cn, co.uk 等
if ($count >= 3) {
$specialSuffix = ['com.cn', 'net.cn', 'co.uk', 'org.cn', 'gov.cn'];
$lastTwo = $parts[$count-2] . '.' . $parts[$count-1];
if (in_array($lastTwo, $specialSuffix)) {
return $parts[$count-3] . '.' . $lastTwo;
}
return $parts[$count-2] . '.' . $parts[$count-1];
}
return $host;
}
echo getRootDomain('sub.example.com.cn'); // 输出:example.com.cn
?>
公共后缀列表方案的局限性
提到更标准的做法,业界常提及管理公共后缀列表(Public Suffix List),它收录了全球所有顶级域名和公开后缀,但直接用PHP处理这个列表代码量较大,且需要定期更新数据,对于大多数业务系统来说,

维护一份常见多级后缀数组已经够用,不必过度设计。
从任意URL字符串提取域名
如果你手里的输入是完整的URL字符串而不是$_SERVER变量,直接用parse_url一步到位:
<?php $url = 'https://user:pass@blog.example.com:8080/path?arg=1#frag'; $host = parse_url($url, PHP_URL_HOST); echo $host; // 输出:blog.example.com ?>
由此可见,在php获取域名的方法中选择parse_url处理用户输入的URL是最谨慎的,因为它不会触发DNS解析,也不会对非法格式报错,这对过滤外部数据非常有用。
php获取当前域名在命令行和框架中的特例
在用PHP编写定时脚本、队列消费者或Artisan命令时,$_SERVER里的HTTP字段为NULL,这时需要用另一种思路。
CLI模式下获取配置域名
CLI模式下没有HTTP请求,获取域名没有统一标准,常用做法是读取配置文件:
<?php // 假设有个配置项 setup.php $config = require __DIR__ . '/config/app.php'; $domain = $config['domain']; // 从配置文件读取 echo $domain; ?>
如果你使用ThinkPHP6/8或Laravel,框架自身提供了处理这个需求的方案:
- ThinkPHP:使用
request()->domain()方法,底层会优先读取.env文件中的APP_DOMAIN配置 - Laravel:使用
url()->to('/'),在CLI环境下它依赖于APP_URL环境变量
从上述两种框架的实现思路可以看出,规范生产环境中免依赖域名的编程方式才是消除此类差异的根治方案,除非绝对必要,在CLI脚本中直接依赖HTTP_HOST是坏味道,会让代码在计划任务和队列进程中报错。
隐藏端口和默认端口处理
当HTTP_HOST返回example.com:80或example.com:443时,你大概率希望将默认端口去掉:
<?php
function cleanHost($host) {
$host = strtolower($host);
if (strpos($host, ':80') !== false && strpos($host, ':443') === false) {
$host = substr($host, 0, -3); // 去掉 :80
}
return $host;
}
?>
更好的方式是结合parse_url:
<?php
$host = $_SERVER['HTTP_HOST'];
$hostInfo = parse_url('http://' . $host);
// https场景下若端口是443,parse_url也能自动识别
if (isset($hostInfo['port']) && $hostInfo['port'] == 80 && empty($_SERVER['HTTPS'])) {
$host = $hostInfo['host'];
}
?>
HTTPS和端口处理的优先级
这里要理清一个逻辑:端口是协议的一部分,不是域名的一部分,获取域名的核心理念永远是”拿到host”,关于是带协议还是带端口,取决于用途:
- 设置Cookie的Domain时:不带端口,不带协议
- 生成页面绝对链接时:带协议和完整域名
- 给前端做跨域白名单时:只保留协议和根域名,去掉具体路径

如何防范域名获取中的安全性问题
使用HTTP_HOST有一个无法回避的风险Host头攻击,攻击者可以篡改请求中的Host字段来伪造域名,这样生成的链接就可能被用于钓鱼。
Host头注入的简单防护算法
在处理HTTP_HOST之前,必须做白名单校验:
<?php
$allowedDomains = ['www.example.com', 'example.com', 'admin.example.com'];
$host = $_SERVER['HTTP_HOST'];
if (!in_array($host, $allowedDomains, true)) {
http_response_code(400);
exit('Bad Request');
}
?>
如果项目在公网环境访问,且域名不固定(比如用户绑定了自己的域名),至少应该校验域名格式:
<?php
function isValidDomain($host) {
// 只允许字母、数字、连字符和点
return preg_match('/^([a-z0-9-]+.)+[a-z0-9-]+$/i', $host) && strlen($host) < 255;
}
?>
输出时做好转义
在页面上输出域名前,建议使用htmlspecialchars()包裹,防止恶意脚本通过伪造Host字段注入HTML标签,大多数现代PHP框架已经默认在模板引擎层做了转义,但如果你用的是原生模板或自研框架,这一步绝不能省。
常见问题与解答(php获取域名相关)
php获取服务器ip和域名的代码能一起写吗?
可以,获取IP用$_SERVER['SERVER_ADDR']或gethostbyname(php_uname('n')),获取域名用前述方法,两者间没有依赖关系,但如果你想根据IP反向解析出域名,PHP没有内置函数直接实现,需要借助gethostbyaddr()函数,它可能发起DNS PTR查询,性能上存在损耗,应避免在循环中调用。
php获取当前域名时,$_SERVER['HTTP_HOST']和$_SERVER['SERVER_NAME']选哪个更好?
选HTTP_HOST更符合用户实际访问地址,但安全性稍差;选SERVER_NAME更稳定,但多域名绑定服务器时可能返回的不是用户访问的那个域名,据PHP官方文档描述,SERVER_NAME在多虚拟主机环境下受Apache UseCanonicalName配置影响,该值可能被强制改写为ServerName指令指定的域名,如果你在写框架底层,建议封装一层方法,默认用HTTP_HOST,提供参数让开发者切换到SERVER_NAME。
thinkphp获取当前域名怎么写?
ThinkPHP5.1及以上版本使用request()->host()获取域名(不含协议),或使用request()->domain()获取带协议的完整域名,在ThinkPHP6中,建议使用app()->request->host(),如果你的版本是从控制器内调用,写法为$this->request->host(),注意在中间件或服务中,request()助手函数同样可用,不必依赖$_SERVER直接操作。
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/784880.html

