简体   繁体   English

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

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

What is the difference between $_SERVER['HTTP_HOST'] and $_SERVER['SERVER_NAME'] in PHP? PHP 中$_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME']有什么区别?

When would you consider using one over the other and why?您什么时候会考虑使用一个而不是另一个?为什么?

The HTTP_HOST is obtained from theHTTP request header and this is what the client actually used as "target host" of the request. HTTP_HOST是从HTTP 请求头中获得的,这是客户端实际用作请求的“目标主机”的内容。 The SERVER_NAME is defined in server config. SERVER_NAME在服务器配置中定义。 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.但是,您需要确保有问题的网络服务器正确配置了SERVER_NAME Taking Apache HTTPD as an example, here's an extract from its documentation :以 Apache HTTPD 为例,以下是其文档的摘录:

If no ServerName is specified, then the server attempts to deduce the hostname by performing a reverse lookup on the IP address.如果未指定ServerName ,则服务器尝试通过对 IP 地址执行反向查找来推断主机名。 If no port is specified in the ServerName , then the server will use the port from the incoming request.如果在ServerName未指定端口,则服务器将使用传入请求中的端口。 For optimal reliability and predictability, you should specify an explicit hostname and port using the ServerName directive.为了获得最佳的可靠性和可预测性,您应该使用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.更新:在检查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标头和测试告诉我这确实(错误地)是这种情况。

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.在第一次怀疑 PHP 并挖掘有关该主题的一些PHP 错误报告后,我了解到问题的根源在于所使用的 Web 服务器,当请求SERVER_NAME时,它错误地返回了 HTTP Host标头。 So I dug into Apache HTTPD bug reports using various keywords regarding the subject and I finally found a related bug .因此,我使用有关该主题的各种关键字深入研究了Apache HTTPD 错误报告,我终于找到了一个相关的错误 This behaviour was introduced since around Apache HTTPD 1.3.这种行为是从 Apache HTTPD 1.3 开始引入的。 You need to setUseCanonicalName directive to on in the <VirtualHost> entry of the ServerName in httpd.conf (also check the warning at the bottom ofthe document !).您需要在httpd.conf ServerName<VirtualHost>条目中将UseCanonicalName指令设置为on (还要检查文档底部的警告!)。

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

This worked for me.这对我有用。

Summarized, SERVER_NAME is more reliable, but you're dependent on the server config!总之, SERVER_NAME更可靠,但您依赖于服务器配置!

HTTP_HOST is the target host sent by the client. HTTP_HOST是客户端发送的目标主机。 It can be manipulated freely by the user.它可以由用户自由操作。 It's no problem to send a request to your site asking for a HTTP_HOST value of www.stackoverflow.com .这是发送请求到您的网站要求一个没有问题HTTP_HOST的值www.stackoverflow.com

SERVER_NAME comes from the server's VirtualHost definition and is therefore considered more reliable. SERVER_NAME来自服务器的VirtualHost定义,因此被认为更可靠。 It can, however, also be manipulated from outside under certain conditions related to how your web server is set up: See this This SO question that deals with the security aspects of both variations.但是,在与您的 Web 服务器的设置方式相关的某些条件下,它也可以从外部进行操作:请参阅此 SO 问题该问题涉及两种变体的安全方面。

You shouldn't rely on either to be safe.你不应该依赖任何一个来确保安全。 That said, what to use really depends on what you want to do.也就是说,使用什么实际上取决于您想要做什么。 If you want to determine which domain your script is running on, you can safely use HTTP_HOST as long as invalid values coming from a malicious user can't break anything.如果您想确定您的脚本在哪个域上运行,只要来自恶意用户的无效值不能破坏任何内容,您就可以安全地使用HTTP_HOST

As I mentioned in this answer , if the server runs on a port other than 80 (as might be common on a development/intranet machine) then HTTP_HOST contains the port, while SERVER_NAME does not.正如我在这个答案中提到的,如果服务器在 80 以外的端口上运行(在开发/内联网机器上可能很常见),那么HTTP_HOST包含该端口,而SERVER_NAME不包含。

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

(At least that's what I've noticed in Apache port-based virtualhosts) (至少这是我在基于 Apache 端口的虚拟主机中注意到的)

Note that HTTP_HOST does not contain :443 when running on HTTPS (unless you're running on a non-standard port, which I haven't tested).请注意, HTTP_HOST在 HTTPS 上运行时包含:443 (除非您在非标准端口上运行,我还没有测试过)。

As others have noted, the two also differ when using IPv6:正如其他人所指出的,两者在使用 IPv6 时也有所不同:

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

Please note that if you want to use IPv6, you probably want to use HTTP_HOST rather than SERVER_NAME .请注意,如果您想使用 IPv6,您可能想使用HTTP_HOST而不是SERVER_NAME If you enter http://[::1]/ the environment variables will be the following:如果您输入http://[::1]/ ,环境变量将如下所示:

HTTP_HOST = [::1]
SERVER_NAME = ::1

This means, that if you do a mod_rewrite for example, you might get a nasty result.这意味着,例如,如果您执行 mod_rewrite,您可能会得到令人讨厌的结果。 Example for a SSL redirect: 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}/

This applies ONLY if you access the server without an hostname.这仅适用于您在没有主机名的情况下访问服务器。

If you want to check through a server.php or whatever, you want to call it with the following:如果你想通过 server.php 或其他东西检查,你想用以下命令调用它:

<?php
    phpinfo(INFO_VARIABLES);
?>

or或者

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

    print_r($_SERVER);
?>

Then access it with all the valid URLs for your site and check out the difference.然后使用您网站的所有有效 URL 访问它并检查差异。

Depends what I want to find out.取决于我想了解什么。 SERVER_NAME is the host name of the server, whilst HTTP_HOST is the virtual host that the client connected to. SERVER_NAME 是服务器的主机名,而 HTTP_HOST 是客户端连接到的虚拟主机。

Assuming one has a simple setup (CentOS 7, Apache 2.4.x, and PHP 5.6.20) and only one website (not assuming virtual hosting) ...假设有一个简单的设置(CentOS 7、Apache 2.4.x 和 PHP 5.6.20)并且只有一个网站(不假设虚拟主机)......

In the PHP sense, $_SERVER['SERVER_NAME'] is an element PHP registers in the $_SERVER superglobal based on your Apache configuration ( **ServerName** directive with UseCanonicalName On ) in httpd.conf (be it from an included virtual host configuration file, whatever, etc ...).在 PHP 意义上, $_SERVER['SERVER_NAME']是 PHP 根据您在 httpd.conf 中的 Apache 配置(带有UseCanonicalName On **ServerName**指令)在$_SERVER超全局变量中注册的元素(可以是来自包含的虚拟主机)配置文件,无论什么,等等......)。 HTTP_HOST is derived from the HTTP host header. HTTP_HOST派生自 HTTP host标头。 Treat this as user input.将此视为用户输入。 Filter and validate before using.使用前过滤和验证。

Here is an example of where I use $_SERVER['SERVER_NAME'] as the basis for a comparison.这是我使用$_SERVER['SERVER_NAME']作为比较基础的示例。 The following method is from a concrete child class I made named ServerValidator (child of Validator ).以下方法来自我创建的名为ServerValidatorValidator子级)的具体子类。 ServerValidator checks six or seven elements in $_SERVER before using them. ServerValidator在使用它们之前检查 $_SERVER 中的六个或七个元素。

In determining if the HTTP request is POST, I use this method.在确定 HTTP 请求是否为 POST 时,我使用此方法。

public function isPOST()
{
    return (($this->requestMethod === 'POST')    &&  // Ignore
            $this->hasTokenTimeLeft()            &&  // Ignore
            $this->hasSameGETandPOSTIdentities() &&  // Ingore
            ($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}

By the time this method is called, all filtering and validating of relevant $_SERVER elements would have occurred (and relevant properties set).到调用此方法时,相关 $_SERVER 元素的所有过滤和验证都会发生(并设置相关属性)。

The line ...线...

($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')

... checks that the $_SERVER['HTTP_HOST'] value (ultimately derived from the requested host HTTP header) matches $_SERVER['SERVER_NAME'] . ... 检查$_SERVER['HTTP_HOST']值(最终派生自请求的host HTTP 标头)是否与$_SERVER['SERVER_NAME']匹配。

Now, I am using superglobal speak to explain my example, but that is just because some people are unfamiliar with INPUT_GET , INPUT_POST , and INPUT_SERVER in regards to filter_input_array() .现在,我使用 superglobal 来解释我的例子,但这只是因为有些人不熟悉INPUT_GETINPUT_POSTINPUT_SERVER关于filter_input_array()

The bottom line is, I do not handle POST requests on my server unless all four conditions are met.最重要的是,除非满足所有四个条件,否则我不会在我的服务器上处理 POST 请求。 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.因此,就 POST 请求而言,未能提供 HTTP host标头(早先的存在性测试)对于严格的HTTP 1.0浏览器来说意味着厄运 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.此外,请求的主机必须与httpd.conf 中的ServerName值相匹配,并且通过扩展与$_SERVER超全局变量中的$_SERVER $_SERVER('SERVER_NAME')值相匹配 Again, I would be using INPUT_SERVER with the PHP filter functions, but you catch my drift.同样,我将使用INPUT_SERVER和 PHP 过滤器函数,但您发现了我的INPUT_SERVER

Keep in mind that Apache frequently uses ServerName in standard redirects (such as leaving the trailing slash off a URL: Example, http://www.example.com becoming http://www.example.com/ ), even if you are not using URL rewriting.请记住,Apache 经常在标准重定向中使用ServerName (例如保留 URL 的尾部斜杠:例如, http://www.example.com变为http://www.example.com/ ),即使您是不使用 URL 重写。

I use $_SERVER['SERVER_NAME'] as the standard, not $_SERVER['HTTP_HOST'] .我使用$_SERVER['SERVER_NAME']作为标准,而不是$_SERVER['HTTP_HOST'] There is a lot of back and forth on this issue.在这个问题上有很多来回。 $_SERVER['HTTP_HOST'] could be empty, so this should not be the basis for creating code conventions such as my public method above. $_SERVER['HTTP_HOST']可能是空的,所以这不应该是创建代码约定的基础,比如我上面的公共方法。 But, just because both may be set does not guarantee they will be equal.但是,仅仅因为两者都可以设置并不能保证它们相等。 Testing is the best way to know for sure (bearing in mind Apache version and PHP version).测试是确定的最好方法(记住 Apache 版本和 PHP 版本)。

It took me a while to understand what people meant by ' SERVER_NAME is more reliable'.我花了一段时间才理解人们所说的“ SERVER_NAME更可靠”是什么意思。 I use a shared server and does not have access to virtual host directives.我使用共享服务器并且无权访问虚拟主机指令。 So, I use mod_rewrite in .htaccess to map different HTTP_HOST s to different directories.因此,我在.htaccess使用 mod_rewrite 将不同的HTTP_HOST映射到不同的目录。 In that case, it is HTTP_HOST that is meaningful.在这种情况下,有意义的是HTTP_HOST

The situation is similar if one uses name-based virtual hosts: the ServerName directive within a virtual host simply says which hostname will be mapped to this virtual host.如果使用基于名称的虚拟主机,情况也类似:虚拟主机中的ServerName指令只是说明哪个主机名将映射到该虚拟主机。 The bottom line is that, in both cases, the hostname provided by the client during the request ( HTTP_HOST ), must be matched with a name within the server, which is itself mapped to a directory.最重要的是,在这两种情况下,客户端在请求期间提供的主机名 ( HTTP_HOST ) 必须与服务器中的名称匹配,该名称本身映射到目录。 Whether the mapping is done with virtual host directives or with htaccess mod_rewrite rules is secondary here.映射是使用虚拟主机指令还是使用 htaccess mod_rewrite 规则完成的在这里是次要的。 In these cases, HTTP_HOST will be the same as SERVER_NAME .在这些情况下, HTTP_HOST将与SERVER_NAME相同。 I am glad that Apache is configured that way.我很高兴 Apache 是这样配置的。

However, the situation is different with IP-based virtual hosts.但是,基于 IP 的虚拟主机的情况有所不同。 In this case and only in this case, SERVER_NAME and HTTP_HOST can be different, because now the client selects the server by the IP, not by the name.在这种情况下并且仅在这种情况下, SERVER_NAMEHTTP_HOST可以不同,因为现在客户端通过 IP 而不是名称来选择服务器。 Indeed, there might be special configurations where this is important.事实上,可能有一些特殊的配置,这很重要。

So, starting from now, I will use SERVER_NAME , just in case my code is ported in these special configurations.所以,从现在开始,我将使用SERVER_NAME ,以防万一我的代码被移植到这些特殊配置中。

As balusC said SERVER_NAME is not reliable and can be changed in apache config , server name config of server and firewall that can be between you and server.正如 balusC 所说 SERVER_NAME 不可靠,可以在 apache config 中更改,服务器和防火墙的服务器名称配置可以在你和服务器之间。

Following function always return real host (user typed host) without port and it's almost reliable:以下函数总是返回没有端口的真实主机(用户输入的主机),它几乎是可靠的:

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

$_SERVER['SERVER_NAME'] is based on your web servers configuration. $_SERVER['SERVER_NAME']基于您的 Web 服务器配置。 $_SERVER['HTTP_HOST'] is based on the request from the client. $_SERVER['HTTP_HOST']基于客户端的请求。

I'm unhappy with all the answers.我对所有答案都不满意。 Some of them are correct but don't tell the whole story and don't make the issue clear.其中一些是正确的,但没有说出全部故事,也没有把问题说清楚。

Regardless of which http server you use the HTTP_HOST should contain the original value sent from the client in the HTTP header Host .无论您使用哪个 http 服务器, HTTP_HOST都应包含从客户端发送的原始值 HTTP header Host And therefore it is user controlled data that should not be trusted.因此,不应信任用户控制的数据。

The variable SERVER_NAME is something that is configured in your server configuration and might not lead to the correct URL. For example there could be a revers proxy in front of your web servers and SERVER_NAME is server1 and server2 but you don't want to redirect your user to server1 but to the user friendly host.变量SERVER_NAME是在您的服务器配置中配置的,可能不会导致正确的 URL。例如,您的 web 服务器前面可能有一个反向代理, SERVER_NAMEserver1server2 ,但您不想重定向您的用户到server1但到用户友好的主机。

As such the HTTP_HOST is the more reliable variable as you might want the host the client requested to reach your PHP application.因此HTTP_HOST是更可靠的变量,因为您可能希望客户端请求的主机到达您的 PHP 应用程序。 You don't need to compare them to make sure this is a valid value (they are not necessarily equal).您不需要比较它们来确保这是一个有效值(它们不一定相等)。 There are two ways to ensure this value is valid:有两种方法可以确保此值有效:

  1. compare the value to a list of valid values (you need to know the valid values)将该值与有效值列表进行比较(您需要知道有效值)
  2. ensure your web server returns an error if the value is not correct如果值不正确,请确保您的 web 服务器返回错误

The first one is easy to understand but can be problematic in real scenarios (in your dev environment it is this, on staging it is this, on production that... etc.).第一个很容易理解,但在实际场景中可能会出现问题(在您的开发环境中是这样,在暂存中是这样,在生产中是这样......等等)。 That means you need to know in PHP what is valid for this environment.这意味着您需要在 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.第二个是服务器配置的东西:VirtualHost 是 http 服务器的概念,可以从同一台服务器提供多个网站。 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.由于虚拟主机是由 http header 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").您需要配置第二个默认虚拟主机(如果没有其他虚拟主机匹配)并始终返回错误(例如“未找到”或“禁止”)。

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

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