背景
前回の記事では、GitLabのexternal_url構成要素の役割を検討し、組み込みNginxのserver_name機能の分析を開始しました。Hostフィールドの重要性について言及したため、引き続き詳細を検証します。
Nginx構成要素の検証
前回の記事で述べた通り、HostはHTTP/1.1プロトコルにおいて必須のリクエストヘッダーフィールドであり、サーバーに対してクライアントがアクセスしたいドメイン名を通知する役割を果たします。ブラウザでWebサービスにアクセスする際、HTTP/1.1リクエストを発行すると自動的にHostヘッダーが追加され、その値にはアドレスバーに入力されたドメイン名(またはIPアドレス+ポート番号)が設定されます。例えば:
http://www.sample-site.comの場合、Hostはwww.sample-site.comhttps://api.myapplication.net/v1/dataの場合、Hostはapi.myapplication.nethttp://192.168.1.50:9090/apiの場合、Hostは192.168.1.50:9090
注意点として、URLにポート番号が含まれている場合(標準の80/443以外)、Hostにはポート番号も含まれます。標準ポートについてはHostヘッダーには記載されません(URLに明示的に記述されていても、ブラウザがHostヘッダーを構築する際には省略されます)。
上記の例から分かるように、Hostリクエストヘッダーの値は、URL中の://以降から最初の/までの部分であり、ドメイン名(例:www.sample-site.com)、またはIPアドレス(例:192.168.1.50)を含みます。ポート番号が標準値(HTTPは80、HTTPSは443)でない場合は、明示的なポート番号も付加されます(例:192.168.1.50:9090)。
この点は公式文書であるHTTP/1.1仕様RFC 7230、Section 5.4にも記載されています。
上記から重要な情報が得られます:
- リクエスト内のHostヘッダーフィールドは、ターゲットURIのホスト名とポート情報を提供します(ユーザーがアクセスしたいサイト名を示しており、具体的なポート情報も含む可能性があります)
- Hostヘッダーフィールドにより、Webサーバーは複数のリソースを識別できます。同一サーバー上には数千のファイル、ページ、APIが存在する可能性がありますが、パスのみでは不十分です。異なるサイトに同じパス名が存在する可能性があるため(例:サイトAの
siteA.com/index.html、サイトBのsiteB.org/index.html)、host/pathすなわちHostとパスの組み合わせによってのみ、ユーザーの要求内容を正確に特定できます。ここでいうHostは、index.htmlパスがどのサイトのものかをサーバーに伝える役割を果たします。 - Hostの機能は、単一のWebサーバー(同一IPアドレス)上で複数のドメインにサービスを提供する状況で特に重要です。これはバーチャルホスティングの中核シナリオです。二つのドメインが同一IPを指している場合、両方のリクエストがこのWebサーバーに送信されますが、サーバーはHostヘッダーによってどのサイトのコンテンツを返すべきかを判別できます。つまり、Hostがなければマルチサイト運用は実現できず、これは現代Webが多数のサイトを低コストで運用できる基盤技術の一つです。
Hostヘッダーの理解が深まったところで、アクセスリクエストとNginx設定ブロック内のserver_nameとの相互作用を見ていきます。
server_name構成要素とHostヘッダーは一致関係にあることがわかります。つまり、server_nameを記述する際の基本ルールはHostヘッダーと同様です:
- まず、標準ポートは必要ないし、記述すべきではありません。例えば:
この記述は誤りであり、Nginxはlocation / { listen 80 default_server; server_name "sample.com:80"; # 問題あり、標準ポートを記述しないこと # その他の設定... }sample.comドメインに一致しません。server_nameから標準ポートを削除してください。location / { listen 80 default_server; server_name "sample.com"; # 正しい記述 # その他の設定... }
- また、Webサービスが非標準ポート(例:
8080)でリッスンしている場合、ユーザーがhttp://sample.com:8080でアクセスする際、Hostヘッダーはsample.com:8080になります。このような場合はlistenとserver_nameを分離して実装できます。location / { listen 8080; server_name "sample.com"; # その他の設定... }
最後にまとめると、HostフィールドはURL中の://から最初の/までの間、プロトコルの標準ポートを除外した内容であり、ブラウザが自動的にHostフィールドを設定します。その後、WebサービスのNginxはこのHostフィールドを使用してserver_name構成要素と一致させることで、単一サーバーで複数ウェブサイトをホストする機能を実現します。