PHP中的HTTP_HOST和SERVER_NAME有什么区别?


533

HTTP_HOSTSERVER_NAMEPHP 和有什么不一样?

哪里:

  • HTTP_POST === $_SERVER['HTTP_HOST']
  • SERVER_NAME === $_SERVER['SERVER_NAME']

您何时会考虑使用另一种,为什么呢?


14
“我通常使用HTTP_HOST,这样用户就可以使用开始时使用的确切主机名。例如,如果我在.com和.org域中拥有相同的站点,则我不想将某人从.org发送到.com,尤其是如果他们可能在.org上拥有登录令牌,如果将它们发送到另一个域就会丢失。” -来自stackoverflow.com/questions/1459739/…的
Yarin

5
@Yarin,不要忘记将的结果列入白名单HTTP_HOST。否则,攻击者可以在HTTP的请求中放入任何Host:,并使服务器接受它。
Pacerier'3

6
初学者:这个问题是指通常通过$_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Answers:


780

所述HTTP_HOST从得到的HTTP请求报头,这就是在客户端实际使用作为请求的“目标主机”。将SERVER_NAME在服务器配置定义。使用哪种取决于您的需要。但是,您现在应该意识到,一个是客户机控制的值,因此对于在业务逻辑中使用可能是不可靠的,而另一个是服务器控制的值,该值更可靠。但是,您需要确保所讨论的Web服务器已SERVER_NAME正确配置。以Apache HTTPD为例,以下是其文档摘录:

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


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

在首先怀疑PHP并挖掘了有关该主题的一些PHP错误报告之后,我了解到问题的根源在于所使用的Web服务器,HostSERVER_NAME请求时它错误地返回了HTTP 标头。因此,我使用有关该主题的各种关键字来研究Apache HTTPD错误报告,最后找到了一个相关的错误。此行为是从Apache HTTPD 1.3开始引入的。你需要设置指令,在该进入中(同时检查在底部的警告文件!)。UseCanonicalNameon<VirtualHost>ServerNamehttpd.conf

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

这对我有用。

总而言之,SERVER_NAME它更可靠,但是您依赖于服务器配置!


5
好的,这解决了我的问题,该问题与OP无关,但相关。我非常担心通过使用浏览器可以提供的任何内容来解决安全问题。这个答案是巨大的帮助。感谢您抽出宝贵的时间来整理。
伊扎克

2
为什么说HTTP_HOST不可靠?是的,它是由用户提供的,但是如果用户提供了一些虚假的值,则您的服务器配置将自动返回503,并且您的PHP脚本甚至都不会运行!
Pacerier 2013年

1
@Pacerier:在编写此答案时,它没有。答案中提到了版本。我不再跟上PHP了,所以我不能说它在新版本中是否确实发生了变化。
BalusC

2
从WinXP中欺骗Apache的一种简单方法是在“主机”文件中添加一行,说明服务器的IP已分配给另一个域,例如:“ 127.0.0.1 mydomain.com”。我已经多次使用它来展示一个本地网站,欺骗我的听众以为我已经建立了互联网连接,并且网站加载速度非常快。您可以采用另一种方法,并使用“ 173.194.41.5 localhost”欺骗Apache以使其在本地运行,因此除非您确定Apache配置正确,否则您永远不应完全信任SERVER_NAME。
vicenteherrera

1
我只想添加,NGINX + PHP-FPM返回server_name指令设置的值。特别是如果没有server_name设置,也_SERVER["SERVER_NAME"]将为空。
white_gecko 2014年

69

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

SERVER_NAME来自服务器的VirtualHost定义,因此被认为更可靠。但是,也可以在与Web服务器的设置有关的某些条件下从外部进行操作:请参阅此SO问题,涉及两个版本的安全性方面。

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


8
是的,但是大多数HTTP服务器都会拒绝请求www.stackoverflow.com的HTTP_HOST值的请求,因此PHP脚本甚至看不到该请求!
Pacerier

2
@Pacerier为true,但如果服务器配置不正确,则并非总是如此。
Pekka

1
如BalusC的帖子所述,当您通过IP访问Apache虚拟主机时,这两个变量都包含IP(默认情况下),而不是实际的服务器名称。您必须UseCanonicalName on在httpd.conf中使用它来强制SERVER_NAME成为实际的服务器名称。
西蒙·伊斯特

@Pekka웃,如果服务器配置不正确,$_SERVER['SERVER_NAME']将无法正常工作。配置错误的服务器将$_SERVER['SERVER_NAME']根据客户端Host:请求的值进行设置。两者相等。
Pacerier,2015年

好的答案,但我不会假设虚拟主机。
安东尼·鲁特里奇

55

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

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

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

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

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

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

2
他们什么时候会解决这种阴险行为?
Pacerier,2015年

27

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

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


1
SiteGround,在其内部HTTP到https重定向代码中,使用https://%{SERVER_NAME}%{REQUEST_URI}
IXN

6

如果要检查server.php或其他内容,请使用以下命令进行调用:

<?php
    phpinfo(INFO_VARIABLES);
?>

要么

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

    print_r($_SERVER);
?>

然后使用您网站的所有有效URL对其进行访问,并检查差异。


5

取决于我要查找的内容。SERVER_NAME是服务器的主机名,而HTTP_HOST是客户端连接到的虚拟主机。


4
不是完全正确的Rowland,SERVER_NAME通常是VirtualHost的名称,而不是服务器本身的名称。在Apache中,SERVER_NAME通常使用与相同的值填充HTTP_HOST(请参阅BalusC的答案)。
西蒙·伊斯特

1
@Simon,由于大多数主机现在是VirtualHost,因此“服务器本身”的名称是什么意思?
Pacerier,2015年

如果您正在通过一个网站运行虚拟专用服务器(VPS),则无需假定SERVER_NAME适用于虚拟主机。但是,一个站点仍然可以使用虚拟主机设置。许多人使用共享主机,所以我明白你的意思。
安东尼·鲁特里奇

2

我花了一段时间才了解人们所说的“ SERVER_NAME更可靠”的含义。我使用共享服务器,但无法访问虚拟主机指令。因此,我使用mod_rewrite .htaccess 来将不同的HTTP_HOSTs 映射到不同的目录。在那种情况下,那HTTP_HOST才有意义。

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

但是,基于IP的虚拟主机的情况则不同。在这种情况下,只有在这种情况下,SERVER_NAME并且HTTP_HOST可以有所不同,因为现在客户端是通过IP而不是名称来选择服务器。 确实,在某些重要的地方可能会有特殊的配置。

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


2

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

在PHP的意义上,PHP $_SERVER['SERVER_NAME']$_SERVER基于httpd.conf中的Apache配置(**ServerName**带有指令UseCanonicalName On)在超全局变量中注册的元素(可以从包含的虚拟主机配置文件中获取,等等)。HTTP_HOST派生自HTTP host标头。将此视为用户输入。使用前进行过滤和验证。

这是一个我$_SERVER['SERVER_NAME']用作比较基础的示例。以下方法来自我命名为的具体子类ServerValidator(的子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_GETINPUT_POST以及INPUT_SERVER在问候filter_input_array()

最重要的是,除非满足所有四个条件,否则我不会在服务器上处理POST请求。因此,就POST请求而言,未能提供HTTP host标头(已测试过该功能)对于严格的HTTP 1.0浏览器来说是注定的。此外,该请求的主机必须与价值相匹配的在httpd.conf中通过extention的值,并且,在超全局。同样,我将使用PHP过滤器功能,但是您会发现我的漂移。ServerName$_SERVER('SERVER_NAME')$_SERVERINPUT_SERVER

请记住,Apache经常ServerName标准重定向中使用(例如,在URL末尾添加斜杠:例如,http : //www.foo.com变成http://www.foo.com/),即使您不是使用URL重写。

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


0

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

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

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

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.