$_SERVER['HTTP_HOST']和$_SERVER['SERVER_NAME']在PHP中的区别是什么?

什么时候你会考虑使用其中一种而不是另一种,为什么?


当前回答

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

其他回答

假设你有一个简单的设置(CentOS 7, Apache 2.4。x和PHP 5.6.20),只有一个网站(不假设虚拟主机)…

在PHP的意义上,$_SERVER['SERVER_NAME']是PHP在$_SERVER超全局中注册的一个元素,基于httpd.conf中的Apache配置(**ServerName** directive with UseCanonicalName on)(可能来自包含的虚拟主机配置文件,等等…)HTTP_HOST派生自HTTP主机头。将其视为用户输入。使用前进行筛选和验证。

下面是一个例子,我使用$_SERVER['SERVER_NAME']作为比较的基础。下面的方法来自我创建的名为ServerValidator (Validator的子类)的具体子类。ServerValidator在使用$_SERVER中的6或7个元素之前检查它们。

在确定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']值(最终从请求的主机HTTP报头派生)是否与$_SERVER['SERVER_NAME']匹配。

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

The bottom line is, I do not handle POST requests on my server unless all four conditions are met. Hence, in terms of POST requests, failure to provide an HTTP host header (presence tested for earlier) spells doom for strict HTTP 1.0 browsers. Moreover, the requested host must match the value for ServerName in the httpd.conf, and, by extention, the value for $_SERVER('SERVER_NAME') in the $_SERVER superglobal. Again, I would be using INPUT_SERVER with the PHP filter functions, but you catch my drift.

请记住,Apache经常在标准重定向中使用ServerName(例如,将URL后面的斜杠去掉:例如,http://www.example.com变成http://www.example.com/),即使您没有使用URL重写。

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

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

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

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

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

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

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

The second one is something for the server configuration: VirtualHost is a concept for http servers to deliver multiple websites from the same server. As the virtual host is chosen by the http header Host (case insensitive) the client can not control which virtual host is used unless modifying the host. When only one virtual host is configured every value will use this virtual host. You need to configure a second virtual host that is the default (if no other virtual host matches) and always returns an error (for example "not found" or "forbidden").

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

The HTTP_HOST is obtained from the HTTP request header and this is what the client actually used as "target host" of the request. The SERVER_NAME is defined in server config. Which one to use depends on what you need it for. You should now however realize that the one is a client-controlled value which may thus not be reliable for use in business logic and the other is a server-controlled value which is more reliable. You however need to ensure that the webserver in question has the SERVER_NAME correctly configured. Taking Apache HTTPD as an example, here's an extract from its documentation:

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


Update: after checking the answer of Pekka on your question which contains a link to bobince's answer that PHP would always return HTTP_HOST's value for SERVER_NAME, which goes against my own PHP 4.x + Apache HTTPD 1.2.x experiences from a couple of years ago, I blew some dust from my current XAMPP environment on Windows XP (Apache HTTPD 2.2.1 with PHP 5.2.8), started it, created a PHP page which prints the both values, created a Java test application using URLConnection to modify the Host header and tests taught me that this is indeed (incorrectly) the case.

After first suspecting PHP and digging in some PHP bug reports regarding the subject, I learned that the root of the problem is in web server used, that it incorrectly returned HTTP Host header when SERVER_NAME was requested. So I dug into Apache HTTPD bug reports using various keywords regarding the subject and I finally found a related bug. This behaviour was introduced since around Apache HTTPD 1.3. You need to set UseCanonicalName directive to on in the <VirtualHost> entry of the ServerName in httpd.conf (also check the warning at the bottom of the document!).

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

这对我很管用。

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

HTTP_HOST是客户端发送的目标主机。用户可以自由操作。向站点发送请求请求HTTP_HOST值www.stackoverflow.com是没有问题的。

SERVER_NAME来自服务器的VirtualHost定义,因此被认为更可靠。然而,它也可以在与您的web服务器设置相关的某些条件下从外部操纵:请看这个SO问题,处理这两种变体的安全方面。

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