However, the situation is different with IP-based virtual hosts. 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_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/
# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/
9 回答
取决于我想要找到的东西 . SERVER_NAME是服务器的主机名,而HTTP_HOST是客户端连接的虚拟主机 .
由于balusC表示SERVER_NAME不可靠,可以在apache配置中更改,服务器名称配置服务器和防火墙可以在您和服务器之间 .
以下功能始终返回真实主机(用户没有端口的类型主机,它几乎可靠:
HTTP_HOST
是从HTTP request header获得的,这是客户端实际用作请求的"target host"的内容 .SERVER_NAME
在服务器配置中定义 . 使用哪一个取决于您需要它 . 您现在应该意识到,这是一个客户端控制的值,因此在业务逻辑中可能不可靠,另一个是服务器控制的值更可靠 . 但是,您需要确保相关的Web服务器已正确配置SERVER_NAME
. 以Apache HTTPD为例,这里是its documentation的摘录:Update :在检查了包含bobince's answer链接的the answer of Pekka on your question之后,PHP将始终返回
SERVER_NAME
的HTTP_HOST
的值,这与几年前我自己的PHP 4.x Apache HTTPD 1.2.x经验不符,我吹了一些灰尘来自我在Windows XP上的当前XAMPP环境(带有PHP 5.2.8的Apache HTTPD 2.2.1)启动它,创建了一个打印这两个值的PHP页面,使用URLConnection创建了一个Java测试应用程序来修改Host
Headers ,测试告诉我确实(错误地)这种情况 .在首先怀疑PHP并挖掘了一些有关该主题的PHP bug reports之后,我了解到问题的根源在于使用的Web服务器,当请求
SERVER_NAME
时它错误地返回了HTTPHost
标头 . 所以我使用various keywords挖掘了Apache HTTPD bug reports关于这个主题,我终于找到了related bug . 从Apache HTTPD 1.3开始引入了这种行为 . 您需要在httpd.conf
的ServerName
的<VirtualHost>
条目中将UseCanonicalName指令设置为on
(同时检查the document底部的警告!) .这对我有用 .
总结一下,
SERVER_NAME
更可靠,但你在服务器配置上是 dependent !正如我在this answer中提到的,如果服务器在80以外的端口上运行(在开发/内联网机器上可能是常见的),则
HTTP_HOST
包含端口,而SERVER_NAME
则不包含端口 .(至少这是我在基于Apache端口的虚拟主机中注意到的)
请注意,
HTTP_HOST
在HTTPS上运行时不包含:443
(除非您测试了're running on a non-standard port, which I haven') .正如其他人所指出的,这两者在使用IPv6时也有所不同:
HTTP_HOST
是客户端发送的目标主机 . 它可以由用户自由操作 . 向您的网站发送请求HTTP_HOST
的www.stackoverflow.com
值是没问题的 .SERVER_NAME
来自服务器的VirtualHost
定义,因此被认为更可靠 . 但是,它也可以在与Web服务器设置方式相关的某些条件下从外部进行操作:请参阅此This SO question,其中涉及两种变体的安全性方面 .你不应该依赖它们是安全的 . 也就是说,使用什么取决于你想做什么 . 如果要确定运行脚本的域,只要来自恶意用户的无效值不会破坏任何内容,就可以安全地使用
HTTP_HOST
.我花了一段时间才明白“
SERVER_NAME
更可靠”的含义 . 我使用共享服务器,但无法访问虚拟主机指令 . 所以,我在.htaccess
中使用mod_rewrite将不同的HTTP_HOST
映射到不同的目录 . 在这种情况下,它是有意义的HTTP_HOST
.如果使用基于名称的虚拟主机,则情况类似:虚拟主机中的
ServerName
指令只是说明将哪个主机名映射到此虚拟主机 . 最重要的是,在这两种情况下,客户端在请求期间提供的主机名(HTTP_HOST
)必须与服务器中的名称匹配,该名称本身也映射到目录 . 映射是使用虚拟主机指令还是使用htaccess mod_rewrite规则完成,这里是次要的 . 在这些情况下,HTTP_HOST
将与SERVER_NAME
相同 . 我很高兴Apache以这种方式配置 .However, the situation is different with IP-based virtual hosts. 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_NAME
,以防我的代码移植到这些特殊配置中 .假设有一个简单的设置(CentOS 7,Apache 2.4.x和PHP 5.6.20)和只有一个网站(不假设虚拟主机)......
在PHP意义上,
$_SERVER['SERVER_NAME']
是PHP在$_SERVER
超全局中注册的元素,基于您在httpd.conf中的Apache配置(**ServerName**
指令和UseCanonicalName On
)(无论是来自包含的虚拟主机配置文件,等等......) . HTTP_HOST 派生自HTTPhost
标头 . 将此视为用户输入 . 使用前过滤并验证 .以下是我使用
$_SERVER['SERVER_NAME']
作为比较基础的示例 . 以下方法来自我命名为ServerValidator
(Validator
的子项)的具体子类 .ServerValidator
在使用它们之前检查$ _SERVER中的六个或七个元素 .在确定HTTP请求是否为POST时,我使用此方法 .
调用此方法时,将发生相关$ _SERVER元素的所有过滤和验证(并设置相关属性) .
线......
...检查
$_SERVER['HTTP_HOST']
值(最终从请求的host
HTTP标头派生)是否匹配$_SERVER['SERVER_NAME']
.现在,我正在使用superglobal讲话来解释我的例子,但那仅仅是因为有些人对于filter_input_array()不熟悉
INPUT_GET
,INPUT_POST
和INPUT_SERVER
.最重要的是,除非满足所有四个条件,否则我不会在我的服务器上处理POST请求 . 因此,就POST请求而言,未能提供HTTP
host
标头(先前已经过测试)会严格违反 HTTP 1.0 浏览器 . 此外,请求的主机必须匹配httpd.conf中 ServerName 的值,并且,通过扩展,$_SERVER
超全局中的$_SERVER('SERVER_NAME')
的值 . 再次,我将使用INPUT_SERVER
与PHP过滤器功能,但你 grab 了我的漂移 .请记住,Apache经常在标准重定向中使用
ServerName
(例如,从URL中删除尾部斜杠:示例,http://www.foo.com变为http://www.foo.com/),即使您没有使用URL重写 .我使用
$_SERVER['SERVER_NAME']
作为标准,而不是$_SERVER['HTTP_HOST']
. 在这个问题上有很多来回 .$_SERVER['HTTP_HOST']
可能为空,因此这不应该是创建代码约定的基础,例如上面的我的公共方法 . 但是,仅仅因为两者都可以设定并不能保证它们是平等的 . 测试是确定的最佳方式(牢记Apache版本和PHP版本) .请注意,如果要使用IPv6,可能需要使用
HTTP_HOST
而不是SERVER_NAME
. 如果输入http://[::1]/
,则环境变量将如下所示:这意味着,如果你做一个mod_rewrite,你可能会得到一个令人讨厌的结果 . SSL重定向的示例:
仅在您没有主机名的情况下访问服务器时才适用 .
如果你想通过server.php检查或者你想用以下内容调用它:
要么
然后使用您网站的所有有效网址访问它,并查看差异 .