繁体   English   中英

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

[英]What is the difference between HTTP_HOST and SERVER_NAME in PHP?

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

您什么时候会考虑使用一个而不是另一个?为什么?

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

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


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

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

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

这对我有用。

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

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

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

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

正如我在这个答案中提到的,如果服务器在 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'

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

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

如果你想通过 server.php 或其他东西检查,你想用以下命令调用它:

<?php
    phpinfo(INFO_VARIABLES);
?>

或者

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

    print_r($_SERVER);
?>

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

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

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

在 PHP 意义上, $_SERVER['SERVER_NAME']是 PHP 根据您在 httpd.conf 中的 Apache 配置(带有UseCanonicalName On **ServerName**指令)在$_SERVER超全局变量中注册的元素(可以是来自包含的虚拟主机)配置文件,无论什么,等等......)。 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']匹配。

现在,我使用 superglobal 来解释我的例子,但这只是因为有些人不熟悉INPUT_GETINPUT_POSTINPUT_SERVER关于filter_input_array()

最重要的是,除非满足所有四个条件,否则我不会在我的服务器上处理 POST 请求。 因此,就 POST 请求而言,未能提供 HTTP host标头(早先的存在性测试)对于严格的HTTP 1.0浏览器来说意味着厄运 此外,请求的主机必须与httpd.conf 中的ServerName值相匹配,并且通过扩展与$_SERVER超全局变量中的$_SERVER $_SERVER('SERVER_NAME')值相匹配 同样,我将使用INPUT_SERVER和 PHP 过滤器函数,但您发现了我的INPUT_SERVER

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

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

我花了一段时间才理解人们所说的“ 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 ,以防万一我的代码被移植到这些特殊配置中。

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

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

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

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

我对所有答案都不满意。 其中一些是正确的,但没有说出全部故事,也没有把问题说清楚。

无论您使用哪个 http 服务器, HTTP_HOST都应包含从客户端发送的原始值 HTTP header Host 因此,不应信任用户控制的数据。

变量SERVER_NAME是在您的服务器配置中配置的,可能不会导致正确的 URL。例如,您的 web 服务器前面可能有一个反向代理, SERVER_NAMEserver1server2 ,但您不想重定向您的用户到server1但到用户友好的主机。

因此HTTP_HOST是更可靠的变量,因为您可能希望客户端请求的主机到达您的 PHP 应用程序。 您不需要比较它们来确保这是一个有效值(它们不一定相等)。 有两种方法可以确保此值有效:

  1. 将该值与有效值列表进行比较(您需要知道有效值)
  2. 如果值不正确,请确保您的 web 服务器返回错误

第一个很容易理解,但在实际场景中可能会出现问题(在您的开发环境中是这样,在暂存中是这样,在生产中是这样......等等)。 这意味着您需要在 PHP 中知道什么对该环境有效。

第二个是服务器配置的东西:VirtualHost 是 http 服务器的概念,可以从同一台服务器提供多个网站。 由于虚拟主机是由 http header Host选择的(不区分大小写),客户端无法控制使用哪个虚拟主机,除非修改主机。 当只配置一个虚拟主机时,每个值都将使用这个虚拟主机。 您需要配置第二个默认虚拟主机(如果没有其他虚拟主机匹配)并始终返回错误(例如“未找到”或“禁止”)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM