HTTP_HOST获取用户实际访问的域名,来自请求头Host字段,支持端口但可被伪造;SERVER_NAME取自服务器配置,不带端口且与请求无关。

直接记住 $_SERVER['HTTP_HOST'] 和 $_SERVER['SERVER_NAME'] 的区别,比死记所有键名更可靠——多数场景下你要的“域名”其实是前者。
获取用户实际访问的域名用 HTTP_HOST
这是最常用也最贴近真实请求的值,它来自 HTTP 请求头中的 Host 字段,用户在浏览器地址栏输什么、代理怎么转发,它就反映什么。
- 支持端口:比如访问
https://example.com:8080,$_SERVER['HTTP_HOST']就是example.com:8080 - 可能被伪造:如果没做校验,恶意请求可传任意
Host值,生产环境建议白名单过滤 - 空时 fallback:若该键不存在(如 CLI 运行或某些 FastCGI 配置),需判断
isset($_SERVER['HTTP_HOST'])再取值
获取服务器配置的主域名用 SERVER_NAME
这个值来自 Web 服务器(如 Nginx/Apache)的配置,和请求无关,只反映虚拟主机定义的 server_name 或 ServerName。
- 不带端口:即使用户访问
:8080,$_SERVER['SERVER_NAME']通常还是example.com - 不可靠于多站点:同一 IP 多个域名共存时,它返回的是默认虚拟主机的名称,不是当前请求的
- 受
UseCanonicalName影响(Apache):设为On时可能强制返回配置值而非请求头
其他高频键名及典型误用点
不用全记,但得知道哪些容易混淆、何时不该用:
立即学习“PHP免费学习笔记(深入)”;
-
REQUEST_URI:是路径+查询参数(如/path?a=1),不是域名 -
SCRIPT_NAME:是当前 PHP 文件路径(如/index.php),常和重写规则交互,别拿来拼域名 -
PHP_SELF:类似SCRIPT_NAME,但可能被注入污染(如/index.php/),不用于构造 URL -
HTTP_X_FORWARDED_HOST:反向代理场景下可能有值,但不可信,必须结合HTTP_X_FORWARDED_FOR和可信代理列表验证
一个安全拼接完整 URL 的最小实践
如果你真需要“当前页面的完整域名+协议+路径”,别拼凑一堆 $_SERVER 键,优先用已知可靠字段组合,并防御性处理:
// 判断协议(注意:HTTPS 可能由代理设置 X-Forwarded-Proto)
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ||
(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
? 'https' : 'http';
// 取 Host,但限制只允许字母、数字、点、冒号(防 CRLF 注入)
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? 'localhost';
$host = preg_replace('/[^a-zA-Z0-9.:]/', '', $host);
$url = $protocol . '://' . $host . $_SERVER['REQUEST_URI'];
真正麻烦的不是键名记不住,而是没意识到 HTTP_HOST 可被篡改、SERVER_NAME 不反映请求、以及代理环境下各字段的优先级和信任边界。线上服务里,随便用 $_SERVER 拼 URL 导致的跳转劫持或 SSRF,比忘键名严重得多。











