簡體   English   中英

PHP中的HTTP_HOST和SERVER_NAME有什么區別?

[英]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更可靠,但您依賴於服務器配置!

HTTP_HOST是客戶端發送的目標主機。 它可以由用戶自由操作。 這是發送請求到您的網站要求一個沒有問題HTTP_HOST的值www.stackoverflow.com

SERVER_NAME來自服務器的VirtualHost定義,因此被認為更可靠。 但是,在與您的 Web 服務器的設置方式相關的某些條件下,它也可以從外部進行操作:請參閱此 SO 問題該問題涉及兩種變體的安全方面。

你不應該依賴任何一個來確保安全。 也就是說,使用什么實際上取決於您想要做什么。 如果您想確定您的腳本在哪個域上運行,只要來自惡意用戶的無效值不能破壞任何內容,您就可以安全地使用HTTP_HOST

正如我在這個答案中提到的,如果服務器在 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']作為比較基礎的示例。 以下方法來自我創建的名為ServerValidatorValidator子級)的具體子類。 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_GETINPUT_POSTINPUT_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_NAMEHTTP_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_NAMEserver1server2 ,但您不想重定向您的用戶到server1但到用戶友好的主機。

因此HTTP_HOST是更可靠的變量,因為您可能希望客戶端請求的主機到達您的 PHP 應用程序。 您不需要比較它們來確保這是一個有效值(它們不一定相等)。 有兩種方法可以確保此值有效:

  1. 將該值與有效值列表進行比較(您需要知道有效值)
  2. 如果值不正確,請確保您的 web 服務器返回錯誤

第一個很容易理解,但在實際場景中可能會出現問題(在您的開發環境中是這樣,在暫存中是這樣,在生產中是這樣......等等)。 這意味着您需要在 PHP 中知道什么對該環境有效。

第二個是服務器配置的東西:VirtualHost 是 http 服務器的概念,可以從同一台服務器提供多個網站。 由於虛擬主機是由 http header Host選擇的(不區分大小寫),客戶端無法控制使用哪個虛擬主機,除非修改主機。 當只配置一個虛擬主機時,每個值都將使用這個虛擬主機。 您需要配置第二個默認虛擬主機(如果沒有其他虛擬主機匹配)並始終返回錯誤(例如“未找到”或“禁止”)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM