553

$_SERVER['HTTP_HOST']PHP和PHP有什么区别$_SERVER['SERVER_NAME']

您何时会考虑使用其中一种,为什么?

4

10 回答 10

803

HTTP_HOST是从HTTP 请求标头中获得的,这是客户端实际用作请求的“目标主机”的内容。在SERVER_NAME服务器配置中定义。使用哪一个取决于您需要它的用途。但是,您现在应该意识到,一个是客户端控制的值,因此在业务逻辑中使用可能不可靠,另一个是更可靠的服务器控制的值。但是,您需要确保有问题的网络服务器已SERVER_NAME正确配置。以 Apache HTTPD 为例,以下是其文档的摘录:

如果未ServerName指定,则服务器尝试通过对 IP 地址执行反向查找来推断主机名。如果在 中未指定端口ServerName,则服务器将使用传入请求中的端口。ServerName为了获得最佳的可靠性和可预测性,您应该使用该指令指定明确的主机名和端口。


更新:在检查Pekka 对您的问题的回答后,该问题包含一个指向bobince 答案的链接,PHP 将始终返回HTTP_HOST的值SERVER_NAME,这与我几年前的 PHP 4.x + Apache HTTPD 1.2.x 经验背道而驰,我从我当前在 Windows XP 上的 XAMPP 环境(Apache HTTPD 2.2.1 和 PHP 5.2.8)吹了一些灰尘,启动它,创建了一个打印这两个值的 PHP 页面,创建了一个 Java 测试应用程序,URLConnection用于修改Host标题和测试告诉我,这确实是(错误地)情况。

在第一次怀疑 PHP 并挖掘了一些关于该主题的PHP 错误报告后,我了解到问题的根源在于使用的 Web 服务器,它在请求Host时错误地返回了 HTTP 标头。SERVER_NAME因此,我使用有关该主题的各种关键字深入研究了Apache HTTPD 错误报告,最终发现了一个相关的错误。这种行为是在 Apache HTTPD 1.3 前后引入的。您需要在 in 的条目中设置指令(还要检查文档底部的警告!)。UseCanonicalNameon<VirtualHost>ServerNamehttpd.conf

<VirtualHost *>
    ServerName example.com
    UseCanonicalName on
</VirtualHost> 

这对我有用。

总结,SERVER_NAME更可靠,但你依赖于服务器配置!

于 2010-02-19T15:32:25.440 回答
71

HTTP_HOST是客户端发送的目标主机。它可以由用户自由操作。向您的站点发送请求以HTTP_HOST获取www.stackoverflow.com.

SERVER_NAME来自服务器的VirtualHost定义,因此被认为更可靠。但是,它也可以在与您的 Web 服务器的设置方式相关的某些条件下从外部进行操作:请参阅this SO question ,该问题涉及两种变体的安全方面。

你不应该依赖任何一个来保证安全。也就是说,使用什么实际上取决于您想要做什么。如果您想确定您的脚本在哪个域上运行,HTTP_HOST只要来自恶意用户的无效值不会破坏任何内容,您就可以安全地使用。

于 2010-02-19T15:35:01.470 回答
56

正如我在这个答案中提到的,如果服务器在 80 以外的端口上运行(这在开发/内部网机器上可能很常见)则HTTP_HOST包含该端口,而SERVER_NAME没有。

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(至少这是我在基于 Apache 端口的虚拟主机中注意到的)

请注意,在 HTTPS 上运行时不HTTP_HOST包含(除非您在非标准端口上运行,我还没有测试过)。:443

正如其他人所指出的,两者在使用 IPv6 时也有所不同:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
于 2013-08-20T00:22:31.367 回答
28

请注意,如果您想使用 IPv6,您可能想使用HTTP_HOST而不是SERVER_NAME. 如果输入http://[::1]/环境变量会如下:

HTTP_HOST = [::1]
SERVER_NAME = ::1

这意味着,例如,如果您执行 mod_rewrite,您可能会得到一个令人讨厌的结果。SSL 重定向示例:

# SERVER_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/

# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/

这仅适用于您访问没有主机名的服务器。

于 2012-02-27T01:24:37.873 回答
7

如果你想检查 server.php 或其他什么,你想用以下方法调用它:

<?php
    phpinfo(INFO_VARIABLES);
?>

或者

<?php
    header("Content-type: text/plain");

    print_r($_SERVER);
?>

然后使用您网站的所有有效 URL 访问它并检查差异。

于 2010-05-24T07:58:15.437 回答
5

取决于我想知道什么。SERVER_NAME 是服务器的主机名,而 HTTP_HOST 是客户端连接的虚拟主机。

于 2010-02-19T15:31:32.303 回答
2

我花了一段时间才明白人们所说的“SERVER_NAME更可靠”是什么意思。我使用共享服务器并且无权访问虚拟主机指令。因此,我使用 mod_rewrite in.htaccess 将不同的 s 映射HTTP_HOST到不同的目录。在那种情况下,它HTTP_HOST是有意义的。

如果使用基于名称的虚拟主机,情况类似:虚拟主机中的ServerName指令只是说明将哪个主机名映射到该虚拟主机。底线是,在这两种情况下,客户端在请求期间提供的主机名 ( HTTP_HOST) 必须与服务器内的名称匹配,该名称本身映射到目录。映射是使用虚拟主机指令还是使用 htaccess mod_rewrite 规则完成是次要的。在这些情况下,HTTP_HOST将与 相同SERVER_NAME。我很高兴 Apache 是这样配置的。

但是,基于 IP 的虚拟主机的情况有所不同。在这种情况下和仅在这种情况下,SERVER_NAME并且HTTP_HOST可以不同,因为现在客户端通过 IP 选择服务器,而不是通过名称。 实际上,可能有一些特殊的配置很重要。

所以,从现在开始,我将使用SERVER_NAME,以防我的代码被移植到这些特殊配置中。

于 2012-02-16T21:08:13.603 回答
2

假设一个有一个简单的设置(CentOS 7、Apache 2.4.x 和 PHP 5.6.20)并且只有一个网站(不假设虚拟主机)......

在 PHP 的意义上, PHP 是基于您在 httpd.conf 中的 Apache 配置(指令 with )在超全局$_SERVER['SERVER_NAME']中注册的一个元素(无论是来自包含的虚拟主机配置文件,等等......)。HTTP_HOST派生自 HTTP标头。将此视为用户输入。使用前过滤和验证。$_SERVER**ServerName**UseCanonicalName Onhost

这是我$_SERVER['SERVER_NAME']用作比较基础的示例。以下方法来自我创建的具体子类ServerValidator(child of Validator)。ServerValidator在使用它们之前检查 $_SERVER 中的六个或七个元素。

在确定 HTTP 请求是否为 POST 时,我使用此方法。

public function isPOST()
{
    return (($this->requestMethod === 'POST')    &&  // Ignore
            $this->hasTokenTimeLeft()            &&  // Ignore
            $this->hasSameGETandPOSTIdentities() &&  // Ingore
            ($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}

到调用此方法时,相关 $_SERVER 元素的所有过滤和验证都将发生(以及相关属性集)。

线...

($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')

... 检查$_SERVER['HTTP_HOST']值(最终从请求的hostHTTP 标头派生)是否匹配$_SERVER['SERVER_NAME'].

现在,我用超全球说话来解释我的例子,但这只是因为有些人不熟悉INPUT_GET,INPUT_POSTINPUT_SERVER关于filter_input_array().

底线是,除非满足所有四个条件,否则我不会在我的服务器上处理 POST 请求。因此,就 POST 请求而言,未能提供 HTTPhost标头(之前的存在测试)对于严格的HTTP 1.0浏览器来说意味着厄运。此外,请求的主机必须与httpd.conf中的值匹配,并且,通过扩展,与超全局中的值匹配。同样,我将使用PHP 过滤器函数,但你明白我的意思。ServerName$_SERVER('SERVER_NAME')$_SERVERINPUT_SERVER

请记住,Apache 经常ServerName标准重定向中使用(例如在 URL 中留下斜杠:例如,http ://www.example.com变为http://www.example.com/),即使您不是使用 URL 重写。

我使用$_SERVER['SERVER_NAME']作为标准,而不是$_SERVER['HTTP_HOST']. 在这个问题上有很多来回。 $_SERVER['HTTP_HOST']可能是空的,所以这不应该是创建代码约定的基础,比如我上面的公共方法。但是,仅仅因为两者都可以设置并不能保证它们是相等的。测试是确定的最好方法(记住 Apache 版本和 PHP 版本)。

于 2017-03-09T05:20:25.640 回答
0

$_SERVER['SERVER_NAME']基于您的 Web 服务器配置。 $_SERVER['HTTP_HOST']基于来自客户端的请求。

于 2020-03-14T05:16:22.887 回答
0

正如balusC所说, SERVER_NAME 不可靠,可以在 apache config 、服务器的服务器名称配置和可以在您和服务器之间的防火墙中更改。

以下函数总是返回没有端口的真实主机(用户键入的主机),它几乎是可靠的:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}
于 2016-05-20T13:21:26.263 回答