2010-02-19 8 views
465

您何时会考虑使用其中一个,为什么?PHP中的HTTP_HOST和SERVER_NAME有什么区别?

+13

“我通常会使用HTTP_HOST,以便用户保持他们开始使用的确切主机名。例如,如果我在.com和.org域中拥有相同的站点,我不想从.org到.com,特别是如果他们可能在.org上登录令牌,他们会丢失,如果发送到其他域。“ - 这和一些其他有趣的点从http://stackoverflow.com/questions/1459739/php-serverhttp-host-vs-serverserver-name-am-i-understanding-the-ma/1461430#1461430 – Yarin 2011-10-31 21:51:42

+4

@Yarin,唐不要忘记[白名单 - 验证'HTTP_HOST'的结果](http://stackoverflow.com/a/28889208/632951)。否则,攻击者可以在HTTP的“Host:”请求中输入任何***值,并让服务器接受它。 – Pacerier 2015-03-05 22:53:29

+2

初学者:这个问题是指通常通过'$ _SERVER ['HTTP_HOST']'或$ _SERVER ['SERVER_NAME']' – 2017-03-24 21:31:33

回答

706

HTTP_HOSTHTTP request header获得,这是客户端实际用作请求的“目标主机”的内容。 SERVER_NAME在服务器配置中定义。使用哪一个取决于你需要的东西。然而,您现在应该意识到,这是一个客户端控制的值,因此可能不适用于业务逻辑,另一个是服务器控制的值更可靠。但是,您需要确保相关的网络服务器正确配置了SERVER_NAME。以Apache的HTTPD为例,这里是从its documentation的摘录:

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


更新:检查the answer of Pekka on your question其中包含一个链接到bobince's answer后PHP会总是返回HTTP_HOST的价值SERVER_NAME,这违背了我自己的PHP 4.x版+ apache httpd的1.2.x的经验从几年前开始,我在Windows XP(Apache HTTPD 2.2.1,PHP 5.2.8)中使用了当前XAMPP环境中的一些灰尘,启动它,创建了一个打印这两个值的PHP页面,创建了一个Java测试应用程序使用URLConnection修改Host标题,测试告诉我这确实(不正确)。

第一怀疑PHP和挖掘一些PHP bug reports就这一课题后,我才知道,问题的根源是在使用Web服务器,它错误地返回的HTTP标头Host时请求SERVER_NAME。因此,我使用various keywords关于该主题挖掘了Apache HTTPD bug reports,并且我终于找到related bug。此行为是自Apache HTTPD 1.3开始引入的。您需要将UseCanonicalName指令设置为on,<VirtualHost>输入ServerName,httpd.conf(同时检查the document底部的警告!)。

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

这对我有效。

总结出来,SERVER_NAME更可靠,但是你在上依赖在服务器上配置!

+2

好的,这解决了我的问题,这与OP无关,但相关。使用浏览器可以提供的任何东西,我非常关心安全问题。这个答案是一个巨大的帮助。感谢您花时间把它放在一起。 – Yitzhak 2013-03-05 05:08:44

+2

为什么你说HTTP_HOST不可靠?是的,它是由用户提供的,但如果用户给出一些虚假的值,你的服务器配置会自动返回503,你的PHP脚本甚至不会运行! – Pacerier 2013-07-14 15:01:49

+0

@Prier:在写这个答案时,它没有。答案中提到了版本。我不再跟踪PHP了,所以我不能说它是否确实在更新的版本中发生了变化。 – BalusC 2013-07-14 15:03:02

3

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

+2

获得的值不完全正确的Rowland,SERVER_NAME通常是VirtualHost的名称,而不是服务器本身。而在Apache中,'SERVER_NAME'通常被填入与'HTTP_HOST'相同的值(参见BalusC的回答)。 – 2013-08-20 00:24:59

+0

@Simon,因为大多数主机现在都是VirtualHost,你认为“服务器本身”的名称是什么意思? – Pacerier 2015-03-05 23:23:24

+0

如果您正在一个网站上运行虚拟专用服务器(VPS),则不需要假定“SERVER_NAME”适用于虚拟主机。但是,仍然可以为一个站点使用虚拟主机设置。许多人使用共享主机,所以我明白你的观点。 – 2017-03-09 05:38:52

57

HTTP_HOST是客户端发送的目标主机。它可以由用户自由操纵。将请求发送到您的网站要求HTTP_HOST的值为www.stackoverflow.com是没有问题的。

SERVER_NAME来自服务器的VirtualHost定义,因此被认为更可靠。但是,它也可以在与您的Web服务器设置相关的特定条件下从外部操作:请参阅此处理这两个变体的安全方面的This SO question

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

+6

是的,但是一个请求www.stackoverflow.com的HTTP_HOST值的请求会被大多数HTTP服务器拒绝,所以PHP脚本甚至不会看到请求! – Pacerier 2013-07-14 15:04:40

+1

@Pacerier true,但并不总是如果服务器没有正确配置。 – 2013-07-14 15:18:39

+0

正如在BalusC的文章中提到的,当您通过IP访问Apache虚拟主机时,*这两个变量都包含IP(默认情况下),而不是实际的服务器名称。您必须在httpd.conf中使用'UseCanonicalName on'来强制'SERVER_NAME'成为实际的服务器名称。 – 2013-08-20 00:17:27

4

,如果你想通过server.php或者你想叫它什么都检查以下内容:

<?php 

phpinfo(INFO_VARIABLES); 

?> 

<?php 

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

print_r($_SERVER); 

?> 

然后用所有有效网址访问您的网站,并检查出差异。

2

我花了一段时间来理解人的意思“SERVER_NAME更可靠”。我使用共享服务器,并且无法访问虚拟主机指令。因此,我使用.htaccess中的mod_rewrite将不同的HTTP_HOST映射到不同的目录。在这种情况下,这是有意义的HTTP_HOST

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

不过,这种情况是与基于IP的虚拟主机不同。在这种情况下,只有在这种情况下,SERVER_NAMEHTTP_HOST可以不同,因为现在客户端通过IP选择服务器,而不是通过名称。事实上,这可能是特殊的配置,这很重要。

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

23

请注意,如果你要使用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}/ 

仅当您访问没有主机名的服务器时才适用。

42

正如我在this answer提到的,如果服务器80以外的端口上运行(如可能是一个发展/内联网的计算机上常见的),那么HTTP_HOST包含端口,而SERVER_NAME没有。

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

(至少这是我在Apache的基于端口的virtualhosts已经注意到)

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

正如其他人指出,这两个使用IPv6时也各不相同:

$_SERVER['HTTP_HOST'] == '[::1]' 
$_SERVER['SERVER_NAME'] == '::1' 
+1

他们什么时候能解决这个阴险的行为? – Pacerier 2015-03-05 23:19:16

0

正如balusC说SERVER_NAME是不可靠的,并且可以在Apache的配置改变,服务器和防火墙的服务器名称的配置,可以是间你和服务器。

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

function getRealHost(){ 
    list($realHost,)=explode(':',$_SERVER['HTTP_HOST']); 
    return $realHost; 
} 
1

假设一个有一个简单的设置(CentOS的7,阿帕奇2.4.x的,和PHP 5.6.20)只有一个网站(不假设虚拟主机)...

在PHP的意义,$_SERVER['SERVER_NAME']是一个元素PHP登记在$_SERVER超全局基于在httpd.conf Apache配置(**ServerName**指令与UseCanonicalName On)(无论是从包含的虚拟主机配置文件中,无论如何, tc ...)。 HTTP_HOST源自HTTP host标题。将此视为用户输入。使用前进行过滤和验证。

这里是我使用$_SERVER['SERVER_NAME']作为比较基础的示例。以下方法来自具体的儿童课程,名称为ServerValidatorValidator的孩子)。在使用它们之前,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']值(最终从所请求的host HTTP头衍生)匹配$_SERVER['SERVER_NAME']。现在

,我使用超全局发言,解释我的例子,但这只是因为有些人不熟悉INPUT_GETINPUT_POSTINPUT_SERVER的问候filter_input_array()

底线是,我不处理我的服务器上的POST请求,除非全部满足四个条件。因此,就POST请求而言,对于严格的HTTP 1.0浏览器,未能提供HTTP头(在先测试存在)的拼写厄运。此外,该请求的主机必须将值httpd.conf中ServerName通过extention匹配,并且,在$_SERVER超全局为$_SERVER('SERVER_NAME')值。再次,我将使用INPUT_SERVER与PHP的过滤功能,但你赶上我的漂移。

请阿帕奇经常标准使用ServerName头脑重定向(如离开尾削减掉一个URL:例如,http://www.foo.com成为http://www.foo.com/),即使你不使用URL重写。

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