![](/img/trans.png)
[英]$_SERVER['HTTP_HOST'] AND $_SERVER['SERVER_NAME'] Detection Issue
[英]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
更可靠,但您依賴於服務器配置!
正如我在這個答案中提到的,如果服務器在 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']
作為比較基礎的示例。 以下方法來自我創建的名為ServerValidator
( Validator
子級)的具體子類。 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_GET
、 INPUT_POST
和INPUT_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_NAME
和HTTP_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_NAME
是server1
和server2
,但您不想重定向您的用戶到server1
但到用戶友好的主機。
因此HTTP_HOST
是更可靠的變量,因為您可能希望客戶端請求的主機到達您的 PHP 應用程序。 您不需要比較它們來確保這是一個有效值(它們不一定相等)。 有兩種方法可以確保此值有效:
第一個很容易理解,但在實際場景中可能會出現問題(在您的開發環境中是這樣,在暫存中是這樣,在生產中是這樣......等等)。 這意味着您需要在 PHP 中知道什么對該環境有效。
第二個是服務器配置的東西:VirtualHost 是 http 服務器的概念,可以從同一台服務器提供多個網站。 由於虛擬主機是由 http header Host
選擇的(不區分大小寫),客戶端無法控制使用哪個虛擬主機,除非修改主機。 當只配置一個虛擬主機時,每個值都將使用這個虛擬主機。 您需要配置第二個默認虛擬主機(如果沒有其他虛擬主機匹配)並始終返回錯誤(例如“未找到”或“禁止”)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.