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

What is the difference between HTTP_HOST and SERVER_NAME in PHP?

提问人:Emanuil Rusev 提问时间:2/19/2010 最后编辑:MichaelEmanuil Rusev 更新时间:10/5/2022 访问量:468420

问:

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

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

php apache 服务器变量

评论

16赞 Yarin 11/1/2011
“我通常会选择HTTP_HOST,这样用户就可以保持他们开始使用的确切主机名。例如,如果我在 .com 和 .org 域上有相同的站点,我不想将某人从 .org 发送到.com,特别是如果他们可能在 .org 上有登录令牌,如果发送到另一个域,他们会丢失这些令牌。 - 这个以及 stackoverflow.com/questions/1459739/ 的其他一些有趣的观点......
6赞 Pacerier 3/6/2015
@Yarin,不要忘记将HTTP_HOST的结果列入白名单验证。否则,攻击者可以在 HTTP 请求中输入任何值并让服务器接受它。Host:
7赞 Gregory Cosmo Haun 3/25/2017
初学者:这个问题指的是通常通过 或 获得的值$_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME']

答:

6赞 Rowland Shaw 2/19/2010 #1

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

评论

4赞 Simon East 8/20/2013
不完全是 Rowland,通常是 VirtualHost 的名称,而不是服务器本身。在 Apache 中,通常填充了与 BalusC 相同的值(参见 BalusC 的答案)。SERVER_NAMESERVER_NAMEHTTP_HOST
1赞 Pacerier 3/6/2015
@Simon,既然现在大多数主机都是 VirtualHost,那么“服务器本身”的名称是什么意思?
0赞 Anthony Rutledge 3/9/2017
如果您在一个网站上运行虚拟专用服务器 (VPS),则无需假设这适用于虚拟主机。但是,仍然可以对一个站点使用虚拟主机设置。很多人使用共享主机,所以我明白你的意思。SERVER_NAME
820赞 BalusC 2/19/2010 #2

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

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


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

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

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

这对我有用。

总而言之,比较可靠,但你依赖于服务器配置!SERVER_NAME

评论

5赞 Yitzhak 3/5/2013
好的,这解决了我的问题,它与 OP 无关但相关。我非常担心使用浏览器可以提供的任何内容的安全问题。这个答案是一个巨大的帮助。感谢您抽出宝贵时间将其放在一起。
2赞 Pacerier 7/14/2013
为什么说HTTP_HOST不靠谱?是的,它是由用户提供的,但是如果用户给出了一些虚假的值,您的服务器配置将自动返回 503,您的 PHP 脚本甚至不会运行!
1赞 BalusC 7/14/2013
@Pacerier:在写这个答案的时候,它没有。答案中提到了版本。我不再跟上PHP的步伐了,所以我不能说它是否确实在较新的版本中发生了变化。
2赞 vicenteherrera 10/8/2013
从WinXP欺骗Apache的一种简单方法是在“hosts”文件中添加一行,说明服务器的IP已分配给另一个域,如下所示:“127.0.0.1 mydomain.com”。我多次使用它来展示一个本地网站,欺骗我的观众认为我有互联网连接并且网站加载速度非常快。你可以反其道而行之,用“173.194.41.5 localhost”欺骗Apache,让它认为它在本地运行,所以你永远不应该完全信任SERVER_NAME,除非你确定你的Apache配置得很好。
1赞 white_gecko 6/5/2014
我只想补充一点,NGINX+PHP-FPM 返回指令设置的值。特别是如果设置了 no,也将是空的。server_nameserver_name_SERVER["SERVER_NAME"]
74赞 Pekka 2/19/2010 #3

HTTP_HOST是客户端发送的目标主机。它可以由用户自由操作。向您的网站发送请求请求,要求 .HTTP_HOSTwww.stackoverflow.com

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

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

评论

9赞 Pacerier 7/14/2013
是的,但是请求HTTP_HOST值 www.stackoverflow.com 的请求会被大多数 HTTP 服务器预先拒绝,因此 PHP 脚本甚至看不到该请求!
2赞 Pekka 7/14/2013
@Pacerier true,但如果服务器配置不正确,则并非总是如此。
1赞 Simon East 8/20/2013
正如 BalusC 的文章中提到的,当您通过 IP 访问 Apache 虚拟主机时,这两个变量都包含 IP(默认情况下),而不是实际的服务器名称。您必须在 httpd.conf 中使用强制作为实际的服务器名称。UseCanonicalName onSERVER_NAME
0赞 Pacerier 3/6/2015
@Pekka웃,如果服务器配置不正确,$_SERVER['SERVER_NAME'] 将无法正常工作。配置不当的服务器将根据客户端请求的值进行设置。两者都是平等的。$_SERVER['SERVER_NAME']Host:
0赞 Anthony Rutledge 3/9/2017
很好的答案,但我不会假设虚拟主机。
8赞 stevewh 5/24/2010 #4

如果你想检查一个服务器 .php 或其他什么,你想用以下命令调用它:

<?php
    phpinfo(INFO_VARIABLES);
?>

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

    print_r($_SERVER);
?>

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

2赞 Dominic108 2/17/2012 #5

我花了一段时间才明白人们所说的“更可靠”是什么意思。我使用共享服务器,无法访问虚拟主机指令。因此,我使用 mod_rewrite in 将不同的 s 映射到不同的目录。在这种情况下,这是有意义的。SERVER_NAME.htaccessHTTP_HOSTHTTP_HOST

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

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

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

评论

0赞 iRaS 10/5/2022
则代码可能无法在不是客户端应请求的主机(例如负载均衡器的内部主机)的环境中工作SERVER_NAME
30赞 Daniel Marschall 2/27/2012 #6

请注意,如果您想使用 IPv6,您可能希望使用 而不是 .如果输入环境变量,则为:HTTP_HOSTSERVER_NAMEhttp://[::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赞 IXN 4/3/2018
SiteGround,在他们的内部 http 到 https 重定向代码中,使用https://%{SERVER_NAME}%{REQUEST_URI}
63赞 Simon East 8/20/2013 #7

正如我在此答案中提到的,如果服务器在 80 以外的端口上运行(这在开发/Intranet 机器上可能很常见),则包含该端口,而不包含该端口。HTTP_HOSTSERVER_NAME

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

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

请注意,在 HTTPS 上运行时包含(除非您在非标准端口上运行,我尚未测试过)。HTTP_HOST:443

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

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

评论

2赞 Pacerier 3/6/2015
他们什么时候才能修复这种阴险的行为?
0赞 MSS 5/20/2016 #8

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

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

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}
2赞 Anthony Rutledge 3/9/2017 #9

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

在PHP意义上,PHP根据httpd.conf中的Apache配置(指令)在超全局中注册的元素(无论是来自包含的虚拟主机配置文件,等等......HTTP_HOST 是从 HTTP 标头派生的。将其视为用户输入。使用前进行筛选和验证。$_SERVER['SERVER_NAME']$_SERVER**ServerName**UseCanonicalName Onhost

这是我用作比较基础的一个例子。以下方法来自我制作的一个名为 (child of ) 的具体子类。 在使用 _SERVER 美元之前检查六到七个元素。$_SERVER['SERVER_NAME']ServerValidatorValidatorServerValidator

在确定 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')

...检查值(最终派生自请求的 HTTP 标头)是否与 匹配。$_SERVER['HTTP_HOST']host$_SERVER['SERVER_NAME']

现在,我使用超全局语言来解释我的例子,但这只是因为有些人不熟悉 、 和 filter_input_array())。INPUT_GETINPUT_POSTINPUT_SERVER

最重要的是,除非满足所有四个条件,否则我不会处理服务器上的 POST 请求。因此,就 POST 请求而言,未能提供 HTTP 标头(之前测试过的存在)对于严格的 HTTP 1.0 浏览器来说意味着厄运。此外,请求的主机必须与 httpd.confServerName 的值匹配,并且扩展为 superglobal。同样,我将与 PHP 过滤器函数一起使用,但您抓住了我的漂移。host$_SERVER('SERVER_NAME')$_SERVERINPUT_SERVER

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

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

评论

0赞 iRaS 10/5/2022
HTTP_HOST不必等于SERVER_NAME才有效。这取决于您的设置
0赞 Anthony Rutledge 10/6/2022
@iRaS “但是,仅仅因为两者都可以设置并不能保证它们会相等”
0赞 iRaS 10/6/2022
但你建议它们应该相等才能有一个有效的请求
4赞 Vitalie 3/14/2020 #10

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

1赞 iRaS 10/5/2022 #11

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

无论您使用哪个 http 服务器,都应在 HTTP 标头中包含从客户端发送的原始值。因此,用户控制的数据不应该被信任。HTTP_HOSTHost

该变量是在服务器配置中配置的变量,可能不会指向正确的 URL。例如,在您的 Web 服务器前面可能有一个 revers 代理,并且是并且,但您不想将用户重定向到用户友好的主机。SERVER_NAMESERVER_NAMEserver1server2server1

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

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

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

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