$_SERVER['HTTP_HOST']和$_SERVER['SERVER_NAME']在PHP中的区别是什么?
$_SERVER['HTTP_HOST']
$_SERVER['SERVER_NAME']
什么时候你会考虑使用其中一种而不是另一种,为什么?
看我想知道什么了。SERVER_NAME是服务器的主机名,而HTTP_HOST是客户端连接到的虚拟主机。
HTTP_HOST是从HTTP请求头中获得的,这是客户端实际用作请求的“目标主机”。SERVER_NAME在服务器配置中定义。使用哪一个取决于您需要它做什么。然而,您现在应该意识到,一个是客户端控制的值,因此在业务逻辑中使用可能不可靠,而另一个是服务器控制的值,它更可靠。但是,您需要确保所讨论的web服务器正确配置了SERVER_NAME。以Apache HTTPD为例,下面是它的文档的摘录:
HTTP_HOST
SERVER_NAME
如果没有指定ServerName,那么服务器将尝试通过对IP地址执行反向查找来推断主机名。如果ServerName中没有指定端口,那么服务器将使用传入请求的端口。为了获得最佳的可靠性和可预测性,应该使用ServerName指令指定显式主机名和端口。
ServerName
更新:检查Pekka对你问题的回答后,其中包含一个链接到bobince的回答, PHP将总是返回HTTP_HOST的值为SERVER_NAME,这与我自己的PHP 4。Apache HTTPD 1.2。根据几年前的XAMPP经验,我从Windows XP (Apache HTTPD 2.2.1, PHP 5.2.8)上的当前XAMPP环境中吹了一些灰尘,启动它,创建了一个PHP页面,打印这两个值,使用URLConnection创建了一个Java测试应用程序来修改Host头,测试告诉我这确实是(错误的)情况。
URLConnection
Host
在第一次怀疑PHP和挖掘一些PHP错误报告关于主题后,我了解到问题的根源在于使用的web服务器,当SERVER_NAME被请求时,它错误地返回了HTTP Host报头。所以我深入研究了Apache HTTPD错误报告使用不同的关键词关于主题,我终于找到了SERVER_NAME0。这种行为是在Apache HTTPD 1.3左右引入的。你需要将SERVER_NAME1指令设置为httpd.conf中的ServerName中的<VirtualHost>条目中的on(也检查SERVER_NAME2底部的警告!)
httpd.conf
<VirtualHost>
on
<VirtualHost *> ServerName example.com UseCanonicalName on </VirtualHost>
这对我很管用。
总之,SERVER_NAME更可靠,但您在服务器配置上是依赖 !
HTTP_HOST是客户端发送的目标主机。用户可以自由操作。向您的站点发送请求,要求将HTTP_HOST值设置为www.stackoverflow.com是没有问题的。
www.stackoverflow.com
SERVER_NAME来自服务器的VirtualHost定义,因此被认为更可靠。然而,它也可以在与您的web服务器设置相关的某些条件下从外部操纵:请参阅这个SO问题,它处理了这两个变体的安全方面。
VirtualHost
你不应该依赖任何一个来保证安全。也就是说,用什么真的取决于你想做什么。如果您想确定脚本运行在哪个域上,只要来自恶意用户的无效值不能破坏任何东西,就可以安全地使用HTTP_HOST。
如果你想检查server.php或其他什么,你想用下面的方法调用它:
<?php phpinfo(INFO_VARIABLES); ?>
或
<?php header("Content-type: text/plain"); print_r($_SERVER); ?>
然后使用您站点的所有有效url访问它,并检查差异。
我花了一段时间才明白人们所说的“SERVER_NAME更可靠”是什么意思。我使用共享服务器,不能访问虚拟主机指令。因此,我在.htaccess中使用mod_rewrite将不同的# eyz2映射到不同的目录。在这种情况下,HTTP_HOST是有意义的。
.htaccess
如果使用基于名称的虚拟主机,情况也类似:虚拟主机中的ServerName指令只是说明哪个主机名将映射到这个虚拟主机。底线是,在这两种情况下,客户端在请求期间提供的主机名(HTTP_HOST)必须与服务器中的名称匹配,该名称本身映射到一个目录。映射是用虚拟主机指令完成的还是htaccess mod_rewrite规则完成的在这里是次要的。在这些情况下,HTTP_HOST将与SERVER_NAME相同。我很高兴Apache是这样配置的。
的确,可能有一些特殊的配置很重要。
因此,从现在开始,我将使用SERVER_NAME,以防我的代码被移植到这些特殊配置中。
请注意,如果你想使用IPv6,你可能想使用HTTP_HOST而不是SERVER_NAME。如果你输入http://[::1]/,环境变量将如下:
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}/
这只适用于在没有主机名的情况下访问服务器。
正如我在这个答案中提到的,如果服务器运行在不是80的端口上(在开发/内网机器上可能很常见),那么HTTP_HOST包含端口,而SERVER_NAME不包含端口。
$_SERVER['HTTP_HOST'] == 'localhost:8080' $_SERVER['SERVER_NAME'] == 'localhost'
(至少这是我在Apache基于端口的虚拟主机中注意到的)
注意,在HTTPS上运行时HTTP_HOST 不包含:443(除非您在非标准端口上运行,我还没有测试过)。
:443
正如其他人所指出的,这两者在使用IPv6时也有所不同:
$_SERVER['HTTP_HOST'] == '[::1]' $_SERVER['SERVER_NAME'] == '::1'
正如balusC所说,SERVER_NAME是不可靠的,可以在apache配置中更改,服务器的服务器名配置以及您和服务器之间的防火墙。
以下函数总是返回没有端口的真实主机(用户键入的主机),几乎是可靠的:
function getRealHost(){ list($realHost,)=explode(':',$_SERVER['HTTP_HOST']); return $realHost; }
假设你有一个简单的设置(CentOS 7, Apache 2.4。x和PHP 5.6.20),只有一个网站(不假设虚拟主机)…
在PHP的意义上,$_SERVER['SERVER_NAME']是PHP在httpd.conf中根据Apache配置(**ServerName**指令和UseCanonicalName On)在$_SERVER超全局中注册的元素(可能来自包含的虚拟主机配置文件,等等…)HTTP_HOST派生自HTTP的host报头。将其视为用户输入。使用前进行筛选和验证。
**ServerName**
UseCanonicalName On
$_SERVER
host
下面是一个示例,我使用$_SERVER['SERVER_NAME']作为比较的基础。下面的方法来自我创建的名为ServerValidator (Validator的子类)的具体子类。ServerValidator在使用$_SERVER中的6或7个元素之前检查它们。
ServerValidator
Validator
在确定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_GET, INPUT_POST和INPUT_SERVER。
INPUT_GET
INPUT_POST
INPUT_SERVER
底线是,我不处理我的服务器上的POST请求,除非所有四个条件得到满足。因此,就POST请求而言,未能提供HTTP host报头(前面已经测试过了)将厄运标记为严格的HTTP 1.0浏览器。此外,在ServerName0中为ServerName请求的主机必须与值匹配,以及在$_SERVER超全局中为$_SERVER('SERVER_NAME')的值。同样,我将使用INPUT_SERVER和PHP过滤器函数,但您明白我的意思。
$_SERVER('SERVER_NAME')
请记住,Apache经常在标准的重定向中使用ServerName(例如在URL后面留下斜杠:例如,http://www.example.com变成http://www.example.com/),即使您没有使用URL重写。
我使用$_SERVER['SERVER_NAME']作为标准,而不是$_SERVER['HTTP_HOST']。在这个问题上有很多争论。$_SERVER['HTTP_HOST']可以是空的,所以这不应该作为创建代码约定(如上面的公共方法)的基础。但是,仅仅因为它们都是集合并不能保证它们是相等的。测试是最好的方法(记住Apache版本和PHP版本)。
$ _SERVER(“SERVER_NAME”)是基于您的web服务器配置。 $_SERVER['HTTP_HOST']是基于客户端的请求。
我对所有答案都不满意。他们中的一些人是正确的,但没有讲述整个故事,没有把问题说清楚。
无论您使用哪个http服务器,HTTP_HOST都应该包含从客户端发送的http头Host中的原始值。因此,用户控制的数据不应该被信任。
变量SERVER_NAME是在服务器配置中配置的,它可能不会指向正确的URL。例如,在您的web服务器前面可能有一个反向代理,SERVER_NAME是server1和server2,但您不想将用户重定向到server1,而是重定向到用户友好的主机。
server1
server2
因此,HTTP_HOST是更可靠的变量,因为您可能希望客户端请求的主机访问您的PHP应用程序。您不需要比较它们来确保这是一个有效值(它们不一定相等)。有两种方法确保该值有效:
第一个很容易理解,但在实际场景中可能会有问题(在您的开发环境中是这样,在登台环境中是这样,在生产环境中是这样……等等)。这意味着您需要知道在PHP中什么对这个环境有效。
第二个是关于服务器配置的:VirtualHost是一个概念,用于http服务器从同一台服务器传递多个网站。由于虚拟主机是由http报头Host(不区分大小写)选择的,客户端不能控制使用哪个虚拟主机,除非修改主机。当只配置了一个虚拟主机时,每个值都将使用这个虚拟主机。您需要配置第二个默认的虚拟主机(如果没有其他虚拟主机匹配),并且总是返回一个错误(例如"not found"或“forbidden")。