web-dev-qa-db-ja.com

PHPのHTTP_HostとSERVER_NAMEの違いは何ですか?

いつ他のものを使用することを検討しますか、またその理由は何ですか。

509
Emanuil Rusev

HTTP_HostHTTPリクエストヘッダー から取得され、クライアントが実際にリクエストの「ターゲットホスト」として使用したものです。 SERVER_NAMEはサーバー設定で定義されます。どちらを使用するかは、必要なものによって異なります。ただし、1つはクライアント制御の値であり、したがってビジネスロジックで使用するには信頼できない場合があり、もう1つはより信頼性の高いサーバー制御の値であることを理解する必要があります。ただし、問題のWebサーバーにSERVER_NAMEが正しく構成されていることを確認する必要があります。 Apache HTTPDを例にとると、 そのドキュメント からの抜粋です:

ServerNameが指定されていない場合、サーバーはIPアドレスで逆引きを実行してホスト名を推測しようとします。 ServerNameにポートが指定されていない場合、サーバーは着信要求からのポートを使用します。最適な信頼性と予測可能性のために、ServerNameディレクティブを使用して明示的なホスト名とポートを指定する必要があります。


更新bobince's answer へのリンクを含む あなたの質問に対するPekkaの回答 をチェックした後、PHPは常にHTTP_Hostの値を返します数年前の自分のPHP 4.x + Apache HTTPD 1.2.xの経験に反するSERVER_NAMEの場合、Windows XPの現在のXAMPP環境からほこりを吹き飛ばしました(Apache HTTPD 2.2.1 with PHP 5.2.8)、開始、両方の値を印刷するPHPページを作成、Javaテストアプリケーションを作成 URLConnection は、Hostヘッダーとテストを変更して、これが実際に(誤って)事実であることを教えてくれました。

最初にPHPを疑い、主題に関するいくつかの PHPバグレポート を掘り下げた後、問題の根本は使用されているWebサーバーにあり、間違っていることを知りましたSERVER_NAMEが要求されたときにHTTP Hostヘッダーを返しました。だから私は Apache HTTPDバグレポート を掘り下げて さまざまなキーワード をテーマに使用し、最終的に 関連バグ を見つけました。この動作は、Apache HTTPD 1.3以降に導入されました。 <VirtualHost>UseCanonicalNamehttpd.confエントリで on ディレクティブをServerNameに設定する必要があります( ドキュメント !の下部にある警告も確認してください)。

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

これは私のために働いた。

要約すると、SERVER_NAMEはより信頼性が高いのですが、サーバー構成ではdependentです!

763
BalusC

HTTP_Hostは、クライアントによって送信されたターゲットホストです。ユーザーが自由に操作できます。あなたのサイトにHTTP_Hostの値www.stackoverflow.comを要求するリクエストを送信しても問題ありません。

SERVER_NAMEはサーバーのVirtualHostの定義に由来するため、より信頼性が高いと見なされます。ただし、Webサーバーの設定方法に関連する特定の条件下では、外部からも操作できます。こちらを参照してください。 This SO question 両方のバリエーションの。

安全であるためにはどちらにも頼るべきではありません。とは言っても、使用するものは本当にあなたがやりたいことにかかっています。スクリプトが実行されているドメインを特定したい場合は、悪意のあるユーザーからの無効な値が破損しない限り、HTTP_Hostを安全に使用できます。

63
Pekka 웃

この答え で述べたように、(開発/イントラネットマシンでよく見られるように)サーバーが80以外のポートで動作している場合、HTTP_Hostにはポートが含まれますが、SERVER_NAMEには含まれません。

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

(少なくとも、Apacheのポートベースの仮想ホストで私が気付いたのはそれです)

HTTP_HostnotはHTTPSで実行しているときに:443を含みません(私がテストしていない非標準のポートで実行している場合を除く)。

他の人が指摘したように、この2つはIPv6を使用する場合も異なります。

$_SERVER['HTTP_Host'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
51
Simon East

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}/

これは、ホスト名なしでサーバーにアクセスした場合にのみ適用されます。

26

server.phpをチェックしたい場合、または以下のように呼び出します。

<?php

phpinfo(INFO_VARIABLES);

?>

または

<?php

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

print_r($_SERVER);

?>

それからあなたのサイトのためのすべての有効なURLでそれにアクセスして、違いをチェックしてください。

6
stevewh

私が知りたいことによります。 SERVER_NAMEはサーバーのホスト名、HTTP_Hostはクライアントが接続した仮想ホストです。

4
Rowland Shaw

簡単な設定(CentOS 7、Apache 2.4.x、PHP 5.6.20)で、Webサイトは1つだけ(仮想ホスティングを想定していない)と仮定します。

PHPの意味では、$_SERVER['SERVER_NAME']はhttpd.confのApache設定($_SERVERを含む**ServerName**ディレクティブ)に基づくUseCanonicalName Onスーパーグローバルの要素PHPレジスタです設定ファイルなど、何でも。 HTTP_Hostは、HTTPのHostヘッダーから派生しています。これをユーザー入力として扱います。使用前にフィルタリングして検証します。

これが私が比較の基礎として$_SERVER['SERVER_NAME']を使う例です。次のメソッドは、私が作った具象子クラスからServerValidatorValidatorの子)になります。 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']値(最終的には要求されたHost HTTPヘッダーから派生)が$_SERVER['SERVER_NAME']と一致することを確認します。

今、私はスーパーグローバルスピーチを使って私の例を説明していますが、それは filter_input_array() に関してINPUT_GETINPUT_POST、およびINPUT_SERVERに不慣れな人がいるためです。

要するに、all 4つの条件が満たされない限り、私は自分のサーバー上でPOST要求を処理しません。したがって、POSTリクエストに関しては、厳密なHTTP 1.0ブラウザでは、HTTP Hostヘッダーを提供しなかったこと(以前にテスト済みのプレゼンステスト)がDoomとなります。さらに、httpd.conf内のServerNameに要求されたHost 値と一致する必要があります、および拡張により、$_SERVERスーパーグローバル内の$_SERVER('SERVER_NAME')の値。繰り返しになりますが、私はINPUT_SERVERをPHPフィルター関数と一緒に使用しますが、あなたは私のドリフトに気付くでしょう。

Apacheは標準のリダイレクトでしばしばServerNameを使います(例:http://www.foo.com URLの書き換えを使用していなくても、http://www.foo.com/)。

$_SERVER['SERVER_NAME']ではなく、$_SERVER['HTTP_Host']を標準として使用します。この問題に関してはたくさんのことがあります。 $_SERVER['HTTP_Host']は空になる可能性があるので、上記の私のパブリックメソッドのようなコード規約を作成するための基礎になるべきではありません。しかし、両方が設定されているからといって、それらが等しくなるという保証はありません。テストは確実に知るための最良の方法です(ApacheのバージョンとPHPバージョンを念頭に置いてください)。

2

SERVER_NAME」の意味がより信頼できるものになるまでにはしばらく時間がかかりました。共有サーバーを使用していますが、仮想Hostディレクティブにアクセスできません。そのため、私は.htaccessでmod_rewriteを使用して、異なるHTTP_Hostを異なるディレクトリにマップします。その場合、意味があるのはHTTP_Hostです。

名前ベースの仮想ホストを使用している場合も状況は似ています。仮想ホスト内のServerNameディレクティブは、どのホスト名がこの仮想ホストにマップされるかを単純に示しています。要するに、どちらの場合も、要求中にクライアントによって提供されたホスト名(HTTP_Host)は、サーバー内の名前と一致しなければならず、それ自体がディレクトリにマップされています。マッピングが仮想Hostディレクティブで行われるのかhtaccess mod_rewriteルールで行われるのかは、ここでは二次的です。この場合、HTTP_HostSERVER_NAMEと同じになります。 Apacheがそのように設定されていることをうれしく思います。

しかし、状況はIPベースの仮想ホストでは異なります。この場合とこの場合に限り、SERVER_NAMEHTTP_Hostは異なる可能性があります。これは、クライアントが名前ではなくIPでサーバーを選択するためです。 確かに、これが重要な特別な構成があるかもしれません。

それで、これから始めて、私のコードがこれらの特別な設定で移植された場合に備えて、私はSERVER_NAMEを使います。

2
Dominic108

BalusCがSERVER_NAMEは信頼できないと言ったように、あなたとサーバーの間にあることができるApache設定、サーバーのサーバー名設定およびファイアウォールで変更することができます。

以下の関数は常にポートなしで実際のホスト(ユーザーが入力したホスト)を返し、それはほとんど信頼できます:

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