你什么时候考虑使用一个而不是为什么?
答案 0 :(得分:757)
HTTP_HOST
是从HTTP 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(使用PHP 5.2.8的Apache HTTPD 2.2.1)上从我当前的XAMPP环境中吹了一些灰尘,启动它,创建了一个打印这两个值的PHP页面,使用URLConnection
创建了一个Java测试应用程序来修改Host
标题,测试告诉我这确实(错误地)是这样的。
在第一次怀疑PHP并挖掘了一些关于这个主题的PHP bug reports之后,我了解到问题的根源在于使用的Web服务器,它在{{1}时错误地返回了HTTP Host
标头被要求了。所以我使用Apache HTTPD bug reports挖掘various keywords关于这个主题,我终于找到了related bug。从Apache HTTPD 1.3开始引入了这种行为。您需要在SERVER_NAME
on
的{{1}}条目中将UseCanonicalName
指令设为<VirtualHost>
(同时检查the document底部的警告}!)。
ServerName
这对我有用。
总结一下,httpd.conf
更可靠,但您在服务器配置上依赖!
答案 1 :(得分:63)
HTTP_HOST
是客户端发送的目标主机。它可以由用户自由操作。向您的网站发送请求HTTP_HOST
值为www.stackoverflow.com
的请求没有问题。
SERVER_NAME
来自服务器的VirtualHost
定义,因此被认为更可靠。但是,在与Web服务器设置方式相关的某些条件下,也可以从外部操纵它:请参阅此This SO question,其中涉及两种变体的安全方面。
你不应该依赖任何一个是安全的。也就是说,使用什么取决于你想做什么。如果要确定运行脚本的域,只要来自恶意用户的无效值不会破坏任何内容,就可以安全地使用HTTP_HOST
。
答案 2 :(得分:51)
正如我在this answer中提到的,如果服务器在80以外的端口上运行(在开发/内联网机器上可能是常见的),那么HTTP_HOST
包含端口,而SERVER_NAME
没有。
$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'
(至少这是我在基于Apache端口的虚拟主机中注意到的)
请注意,HTTP_HOST
在HTTPS上运行时不包含:443
(除非您在非标准端口上运行,我尚未测试过)。
正如其他人所说,使用IPv6时两者也有所不同:
$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
答案 3 :(得分:26)
请注意,如果您要使用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}/
仅在访问没有主机名的服务器时才适用。
答案 4 :(得分:6)
如果您想通过server.php查看或者想要使用以下内容调用它:
<?php
phpinfo(INFO_VARIABLES);
?>
或
<?php
header("Content-type: text/plain");
print_r($_SERVER);
?>
然后使用您网站的所有有效网址访问它,并查看差异。
答案 5 :(得分:4)
取决于我想要找到的东西。 SERVER_NAME是服务器的主机名,而HTTP_HOST是客户端连接的虚拟主机。
答案 6 :(得分:2)
我花了一段时间才明白“SERVER_NAME
更可靠”的含义。我使用共享服务器,无法访问虚拟主机指令。因此,我在.htaccess
中使用mod_rewrite将不同的HTTP_HOST
映射到不同的目录。在这种情况下,HTTP_HOST
是有意义的。
如果使用基于名称的虚拟主机,情况类似:虚拟主机中的ServerName
指令只是说明将哪个主机名映射到此虚拟主机。最重要的是,在两种情况下,客户端在请求期间提供的主机名(HTTP_HOST
)必须与服务器中的名称匹配,该名称本身映射到目录。映射是使用虚拟主机指令还是使用htaccess mod_rewrite规则完成,这里是次要的。在这些情况下,HTTP_HOST
将与SERVER_NAME
相同。我很高兴Apache以这种方式配置。
但是,基于IP的虚拟主机的情况不同。在这种情况下,仅在这种情况下,SERVER_NAME
和HTTP_HOST
可以不同,因为现在客户端通过IP选择服务器,而不是名称。确实,可能有特殊的这很重要的配置。
因此,从现在开始,我将使用SERVER_NAME
,以防我的代码移植到这些特殊配置中。
答案 7 :(得分:2)
假设有一个简单的设置(CentOS 7,Apache 2.4.x和PHP 5.6.20)和只有一个网站(不假设虚拟主机)......
在PHP意义上,$_SERVER['SERVER_NAME']
是PHP在$_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']
值(最终来自请求的host
HTTP标头)是否与$_SERVER['SERVER_NAME']
匹配。
现在,我正在使用superglobal说话来解释我的例子,但这仅仅是因为有些人对filter_input_array()
,INPUT_GET
和INPUT_POST
不熟悉{{3} }。
最重要的是,除非满足所有四个条件,否则我不会在我的服务器上处理POST请求。因此,就POST请求而言,未能提供HTTP INPUT_SERVER
标头(先前已经过测试)会对严格的 HTTP 1.0 浏览器产生 doom 。此外,请求的主机必须与 httpd.conf 中 host
的值匹配,并且,通过扩展名, ServerName
超全球中的$_SERVER('SERVER_NAME')
。再次,我将$_SERVER
与PHP过滤器功能一起使用,但是你抓住了我的漂移。
请记住,Apache经常在标准重定向中使用INPUT_SERVER
(例如将结尾的斜杠从网址中删除:示例, http://www.foo.com 成为 http://www.foo.com/ ),即使您没有使用URL重写。
我使用ServerName
作为标准,而不是$_SERVER['SERVER_NAME']
。在这个问题上有很多来回。 $_SERVER['HTTP_HOST']
可能为空,因此这不应该是创建代码约定的基础,例如上面的公共方法。但是,仅仅因为两者都可以设定并不能保证它们是平等的。测试是确定的最佳方式(牢记Apache版本和PHP版本)。
答案 8 :(得分:0)
由于balusC表示SERVER_NAME不可靠,可以在apache配置,服务器名称配置和服务器名称配置中更改,可以在您和服务器之间。
以下功能总是返回没有端口的真实主机(用户键入的主机),它几乎可靠:
function getRealHost(){
list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
return $realHost;
}
答案 9 :(得分:0)
$ _ SERVER ['SERVER_NAME'] 基于您的Web服务器配置。 $ _ SERVER ['HTTP_HOST'] 基于客户端的请求。