Nginxサーバーとロケーションブロック選択アルゴリズムを理解する
序章
Nginxは、世界で最も人気のあるWebサーバーの1つです。 多くの同時クライアント接続で高負荷を正常に処理でき、Webサーバー、メールサーバー、またはリバースプロキシサーバーとして機能できます。
このガイドでは、Nginxがクライアントリクエストを処理する方法を決定する舞台裏の詳細について説明します。 これらのアイデアを理解することで、サーバーとロケーションブロックの設計から当て推量を排除し、リクエストの処理を予測しにくくすることができます。
Nginxブロック構成
Nginxは、さまざまなコンテンツを提供するための構成を、階層構造で存在するブロックに論理的に分割します。 クライアントリクエストが行われるたびに、Nginxはリクエストを処理するために使用する必要がある構成ブロックを決定するプロセスを開始します。 この決定プロセスは、このガイドで説明するものです。
ここで説明する主なブロックは、serverブロックとlocationブロックです。
サーバーブロックは、定義されたタイプのリクエストを処理するために使用される仮想サーバーを定義するNginxの構成のサブセットです。 管理者は多くの場合、複数のサーバーブロックを構成し、要求されたドメイン名、ポート、およびIPアドレスに基づいて、どのブロックがどの接続を処理するかを決定します。
ロケーションブロックはサーバーブロック内にあり、Nginxが親サーバーのさまざまなリソースとURIのリクエストを処理する方法を定義するために使用されます。 URIスペースは、管理者がこれらのブロックを使用して好きな方法で細分化できます。 非常に柔軟なモデルです。
Nginxがリクエストを処理するサーバーブロックを決定する方法
Nginxを使用すると、管理者は個別の仮想Webサーバーインスタンスとして機能する複数のサーバーブロックを定義できるため、要求を満たすためにこれらのサーバーブロックのどれを使用するかを決定する手順が必要です。
これは、可能な限り最良の一致を見つけるために使用される、定義されたチェックシステムを通じて行われます。 このプロセス中にNginxが関係する主なサーバーブロックディレクティブは listen
ディレクティブ、および server_name
指令。
解析 listen
一致する可能性のあるものを見つけるためのディレクティブ
まず、NginxはリクエストのIPアドレスとポートを調べます。 これを listen
要求を解決できる可能性のあるサーバーブロックのリストを作成するための各サーバーのディレクティブ。
The listen
ディレクティブは通常、サーバーブロックが応答するIPアドレスとポートを定義します。 デフォルトでは、 listen
ディレクティブには、のリッスンパラメータが与えられます 0.0.0.0:80
(また 0.0.0.0:8080
Nginxが通常の非rootユーザーによって実行されている場合)。 これにより、これらのブロックはポート80の任意のインターフェイスで要求に応答できますが、このデフォルト値はサーバー選択プロセス内であまり重要ではありません。
The listen
ディレクティブは次のように設定できます。
- IPアドレス/ポートの組み合わせ。
- デフォルトのポート80でリッスンする唯一のIPアドレス。
- そのポート上のすべてのインターフェースをリッスンする唯一のポート。
- Unixソケットへのパス。
最後のオプションは、通常、異なるサーバー間でリクエストを渡す場合にのみ影響します。
リクエストを送信するサーバーブロックを決定しようとすると、Nginxは最初にの特異性に基づいて決定しようとします listen
次のルールを使用したディレクティブ:
- Nginxはすべての「不完全な」を翻訳します
listen
各ブロックをそのIPアドレスとポートで評価できるように、欠落している値をデフォルト値に置き換えることによるディレクティブ。 これらの翻訳のいくつかの例は次のとおりです。- のないブロック
listen
ディレクティブは値を使用します0.0.0.0:80
. - IPアドレスに設定されたブロック
111.111.111.111
ポートがない場合は111.111.111.111:80
- ポートに設定されたブロック
8888
IPアドレスがない場合は0.0.0.0:8888
- のないブロック
- 次に、Nginxは、IPアドレスとポートに基づいて、リクエストに最も具体的に一致するサーバーブロックのリストを収集しようとします。 これは、機能的に使用しているすべてのブロックを意味します
0.0.0.0
特定のIPアドレスをリストする一致するブロックがある場合、そのIPアドレスとして(任意のインターフェイスと一致するため)は選択されません。 いずれの場合も、ポートは正確に一致している必要があります。 - 最も具体的な一致が1つしかない場合は、そのサーバーブロックがリクエストの処理に使用されます。 同じレベルの特異性が一致する複数のサーバーブロックがある場合、Nginxは評価を開始します
server_name
各サーバーブロックのディレクティブ。
Nginxは評価するだけであることを理解することが重要です server_name
で同じレベルの特異性に一致するサーバーブロックを区別する必要がある場合のディレクティブ listen
指令。 たとえば、 example.com
ポートでホストされています 80
の 192.168.1.10
、リクエスト example.com
この例では、 server_name
2番目のブロックのディレクティブ。
server {
listen 192.168.1.10;
. . .
}
server {
listen 80;
server_name example.com;
. . .
}
複数のサーバーブロックが同等の特異性で一致する場合、次のステップは、 server_name
指令。
解析 server_name
一致するものを選択するための指令
次に、同じように具体的なリクエストをさらに評価します listen
ディレクティブ、Nginxはリクエストの Host
ヘッダ。 この値は、クライアントが実際に到達しようとしていたドメインまたはIPアドレスを保持します。
Nginxは、見つけた値に最も一致するものを見つけようとします。 server_name
まだ選択候補である各サーバーブロック内のディレクティブ。 Nginxは、次の式を使用してこれらを評価します。
- Nginxは最初にサーバーブロックを見つけようとします
server_name
の値と一致するHost
リクエストのヘッダー正確に。 これが見つかった場合、関連するブロックがリクエストの処理に使用されます。 完全に一致するものが複数見つかった場合は、最初のが使用されます。 - 完全に一致するものが見つからない場合、Nginxはサーバーブロックを検索しようとします。
server_name
先頭のワイルドカード(*
構成内の名前の先頭)。 見つかった場合、そのブロックはリクエストを処理するために使用されます。 複数の一致が見つかった場合、最長の一致がリクエストの処理に使用されます。 - 先頭のワイルドカードを使用して一致するものが見つからない場合、Nginxはサーバーブロックを検索します。
server_name
末尾のワイルドカード(で終わるサーバー名で示される)を使用して一致する*
構成内)。 見つかった場合、そのブロックはリクエストを処理するために使用されます。 複数の一致が見つかった場合、最長の一致がリクエストの処理に使用されます。 - 末尾のワイルドカードを使用して一致するものが見つからない場合、Nginxは次に、
server_name
正規表現を使用する(~
名前の前)。 最初のserver_name
「Host」ヘッダーに一致する正規表現を使用して、リクエストを処理します。 - 正規表現の一致が見つからない場合、NginxはそのIPアドレスとポートのデフォルトのサーバーブロックを選択します。
各IPアドレス/ポートの組み合わせには、上記の方法で一連のアクションを決定できない場合に使用されるデフォルトのサーバーブロックがあります。 IPアドレス/ポートの組み合わせの場合、これは構成の最初のブロック、またはを含むブロックのいずれかになります。 default_server
の一部としてのオプション listen
ディレクティブ(最初に見つかったアルゴリズムをオーバーライドします)。 一つだけ存在することができます default_server
各IPアドレス/ポートの組み合わせごとの宣言。
例
ある場合 server_name
完全に一致する定義 Host
ヘッダー値。そのサーバーブロックは、要求を処理するために選択されます。
この例では、 Host
リクエストのヘッダーがに設定されました host1.example.com
、2番目のサーバーが選択されます。
server {
listen 80;
server_name *.example.com;
. . .
}
server {
listen 80;
server_name host1.example.com;
. . .
}
完全に一致するものが見つからない場合、Nginxは次に server_name
適合する開始ワイルドカードを使用します。 ワイルドカードで始まる最長の一致が、要求を満たすために選択されます。
この例では、リクエストに Host
のヘッダー www.example.org
、2番目のサーバーブロックが選択されます。
server {
listen 80;
server_name www.example.*;
. . .
}
server {
listen 80;
server_name *.example.org;
. . .
}
server {
listen 80;
server_name *.org;
. . .
}
開始ワイルドカードとの一致が見つからない場合、Nginxは式の最後にワイルドカードを使用して一致が存在するかどうかを確認します。 この時点で、ワイルドカードで終わる最長の一致がリクエストを処理するために選択されます。
たとえば、リクエストに Host
ヘッダーをに設定 www.example.com
、3番目のサーバーブロックが選択されます。
server {
listen 80;
server_name host1.example.com;
. . .
}
server {
listen 80;
server_name example.com;
. . .
}
server {
listen 80;
server_name www.example.*;
. . .
}
ワイルドカードの一致が見つからない場合、Nginxは一致の試行に進みます server_name
正規表現を使用するディレクティブ。 リクエストに応答するために、最初の一致する正規表現が選択されます。
たとえば、 Host
リクエストのヘッダーはに設定されています www.example.com
、次に、要求を満たすために2番目のサーバーブロックが選択されます。
server {
listen 80;
server_name example.com;
. . .
}
server {
listen 80;
server_name ~^(www|host1).*\.example\.com$;
. . .
}
server {
listen 80;
server_name ~^(subdomain|set|www|host1).*\.example\.com$;
. . .
}
上記の手順のいずれも要求を満たすことができない場合、要求は一致するIPアドレスとポートのdefaultサーバーに渡されます。
一致するロケーションブロック
Nginxがリクエストを処理するサーバーブロックを選択するために使用するプロセスと同様に、Nginxには、リクエストの処理に使用するサーバー内のロケーションブロックを決定するための確立されたアルゴリズムもあります。
ロケーションブロックの構文
Nginxがリクエストの処理に使用するロケーションブロックを決定する方法を説明する前に、ロケーションブロックの定義に表示される可能性のある構文のいくつかを見ていきましょう。 ロケーションブロックはサーバーブロック(または他のロケーションブロック)内に存在し、リクエストURI(ドメイン名またはIPアドレス/ポートの後に続くリクエストの一部)の処理方法を決定するために使用されます。
ロケーションブロックは通常、次の形式を取ります。
location optional_modifier location_match {
. . .
}
The location_match
上記では、NginxがリクエストURIをチェックする対象を定義しています。 上記の例の修飾子の有無は、Nginxがロケーションブロックを照合しようとする方法に影響します。 以下の修飾子により、関連するロケーションブロックは次のように解釈されます。
- (なし):修飾子が存在しない場合、場所はプレフィックスの一致として解釈されます。 これは、指定された場所がリクエストURIの先頭と照合され、一致を判断することを意味します。
- = :等号が使用されている場合、リクエストURIが指定された場所と完全に一致すれば、このブロックは一致と見なされます。
- 〜:チルダ修飾子が存在する場合、この場所は大文字と小文字を区別する正規表現の一致として解釈されます。
- 〜* :チルダとアスタリスクの修飾子が使用されている場合、ロケーションブロックは大文字と小文字を区別しない正規表現の一致として解釈されます。
- ^〜:カラットとチルダの修飾子が存在し、このブロックが最適な非正規表現の一致として選択されている場合、正規表現の一致は行われません。
ロケーションブロック構文を示す例
プレフィックスマッチングの例として、次のロケーションブロックを選択して、次のようなリクエストURIに応答することができます。 /site
, /site/page1/index.html
、 また /site/index.html
:
location /site {
. . .
}
正確なリクエストURIの一致を示すために、このブロックは常に次のようなリクエストURIに応答するために使用されます。 /page1
. はに応答するために使用されません /page1/index.html
URIを要求します。 このブロックが選択され、インデックスページを使用してリクエストが実行された場合、リクエストの実際のハンドラーとなる別の場所に内部リダイレクトが行われることに注意してください。
location = /page1 {
. . .
}
大文字と小文字を区別する正規表現として解釈する必要がある場所の例として、このブロックを使用して、 /tortoise.jpg
、ただしではなく /FLOWER.PNG
:
location ~ \.(jpe?g|png|gif|ico)$ {
. . .
}
上記のような大文字と小文字を区別しないマッチングを可能にするブロックを以下に示します。 ここでは、両方 /tortoise.jpg
と /FLOWER.PNG
このブロックで処理できます:
location ~* \.(jpe?g|png|gif|ico)$ {
. . .
}
最後に、このブロックは、正規表現が最良の非正規表現一致であると判断された場合に、正規表現一致が発生するのを防ぎます。 のリクエストを処理できます /costumes/ninja.html
:
location ^~ /costumes {
. . .
}
ご覧のとおり、修飾子はロケーションブロックの解釈方法を示しています。 ただし、これはではなく、Nginxがリクエストの送信先のロケーションブロックを決定するために使用するアルゴリズムを示しています。 次にそれについて説明します。
Nginxがリクエストの処理に使用する場所を選択する方法
Nginxは、サーバーブロックを選択する方法と同様の方法で、リクエストを処理するために使用される場所を選択します。 特定のリクエストに最適なロケーションブロックを決定するプロセスを実行します。 このプロセスを理解することは、Nginxを確実かつ正確に構成できるようにするための重要な要件です。
上記で説明したロケーション宣言のタイプを念頭に置いて、NginxはリクエストURIを各ロケーションと比較することにより、可能なロケーションコンテキストを評価します。 これは、次のアルゴリズムを使用して行われます。
- Nginxは、すべてのプレフィックスベースの場所の一致(正規表現を含まないすべての場所の種類)をチェックすることから始めます。 各場所を完全なリクエストURIと照合します。
- まず、Nginxは完全に一致するものを探します。 を使用してロケーションブロックする場合
=
修飾子がリクエストURIと完全に一致することが判明した場合、このロケーションブロックはリクエストを処理するためにすぐに選択されます。 - 正確でない場合(
=
修飾子)ロケーションブロックの一致が見つかった場合、Nginxは正確でないプレフィックスの評価に進みます。 指定されたリクエストURIに一致する最長のプレフィックス位置を検出し、次のように評価します。- 最長一致のプレフィックスの場所に
^~
修飾子を押すと、Nginxはすぐに検索を終了し、この場所を選択してリクエストを処理します。 - 最長一致のプレフィックス位置がを使用しない場合は、
^~
修飾子を指定すると、検索のフォーカスをシフトできるように、一致はNginxによって今のところ保存されます。
- 最長一致のプレフィックスの場所に
- 最長一致のプレフィックス位置が決定されて保存された後、Nginxは正規表現の位置(大文字と小文字の区別と区別なしの両方)の評価に進みます。 一致する最長のプレフィックス位置
内に正規表現の場所がある場合、Nginxはそれらを正規表現の場所のリストの一番上に移動してチェックします。 次に、Nginxは正規表現の場所と順番に照合を試みます。 リクエストURIに一致するfirst正規表現の場所が、リクエストを処理するためにすぐに選択されます。 - リクエストURIに一致する正規表現の場所が見つからない場合は、以前に保存されたプレフィックスの場所がリクエストを処理するために選択されます。
デフォルトでは、Nginxはプレフィックス一致よりも正規表現一致を提供することを理解することが重要です。 ただし、最初にプレフィックス位置を評価し、管理者がを使用して位置を指定することにより、この傾向を無効にすることができます。 =
と ^~
修飾子。
プレフィックスの場所は通常、最も長く、最も具体的な一致に基づいて選択されますが、最初に一致する場所が見つかると、正規表現の評価が停止されることに注意することも重要です。 これは、構成内での配置が正規表現の場所に大きな影響を与えることを意味します。
最後に、正規表現の一致内は、Nginxが正規表現の場所を評価するときに最長のプレフィックス一致が「行をジャンプ」することを理解することが重要です。 これらは、他の正規表現の一致が考慮される前に、順番に評価されます。 非常に役立つNginx開発者であるMaximDouninが、この投稿で選択アルゴリズムのこの部分について説明しています。
ロケーションブロックの評価はいつ他のロケーションにジャンプしますか?
一般的に、リクエストを処理するためにロケーションブロックが選択されると、その時点以降、リクエストはそのコンテキスト内で完全に処理されます。 選択された場所と継承されたディレクティブのみが、兄弟の場所ブロックからの干渉なしに、要求の処理方法を決定します。
これは、予測可能な方法でロケーションブロックを設計できるようにする一般的なルールですが、選択したロケーション内の特定のディレクティブによって新しいロケーション検索がトリガーされる場合があることを理解することが重要です。 「1つのロケーションブロックのみ」ルールの例外は、リクエストが実際に処理される方法に影響を与える可能性があり、ロケーションブロックを設計するときに期待したものと一致しない場合があります。
このタイプの内部リダイレクトにつながる可能性のあるいくつかのディレクティブは次のとおりです。
- 索引
- try_files
- リライト
- error_page
これらについて簡単に説明します。
The index
ディレクティブがリクエストの処理に使用される場合、ディレクティブは常に内部リダイレクトにつながります。 正確な位置の一致は、アルゴリズムの実行をすぐに終了することによって選択プロセスを高速化するためによく使用されます。 ただし、ディレクトリと完全に一致する場所を作成すると、実際の処理のためにリクエストが別の場所にリダイレクトされる可能性が高くなります。
この例では、最初の場所は次のリクエストURIと一致します。 /exact
、ただし、リクエストを処理するために、 index
ブロックによって継承されたディレクティブは、2番目のブロックへの内部リダイレクトを開始します。
index index.html;
location = /exact {
. . .
}
location / {
. . .
}
上記の場合、本当に最初のブロックにとどまるために実行が必要な場合は、ディレクトリへの要求を満たす別の方法を考え出す必要があります。 たとえば、無効な設定をすることができます index
そのブロックのためにそしてオンにします autoindex
:
location = /exact {
index nothing_will_match;
autoindex on;
}
location / {
. . .
}
これは、 index
コンテキストの切り替えによるものですが、ほとんどの構成ではおそらく役に立ちません。 ほとんどの場合、ディレクトリの完全一致は、リクエストの書き換えなどに役立ちます(これにより、新しい場所の検索も行われます)。
処理場所が再評価される可能性のある別の例は、 try_files
指令。 このディレクティブは、名前付きのファイルまたはディレクトリのセットの存在を確認するようにNginxに指示します。 最後のパラメーターは、Nginxが内部リダイレクトを行うURIにすることができます。
次の構成を検討してください。
root /var/www/main;
location / {
try_files $uri $uri.html $uri/ /fallback/index.html;
}
location /fallback {
root /var/www/another;
}
上記の例では、 /blahblah
、最初の場所が最初にリクエストを取得します。 と呼ばれるファイルを見つけようとします blahblah
の /var/www/main
ディレクトリ。 見つからない場合は、次のファイルを検索してフォローアップします。 blahblah.html
. 次に、というディレクトリがあるかどうかを確認しようとします blahblah/
以内 /var/www/main
ディレクトリ。 これらのすべての試行に失敗すると、にリダイレクトされます /fallback/index.html
. これにより、2番目のロケーションブロックによってキャッチされる別のロケーション検索がトリガーされます。 これはファイルを提供します /var/www/another/fallback/index.html
.
ロケーションブロックのパスオフにつながる可能性のある別のディレクティブは、 rewrite
指令。 使用する場合 last
パラメータと rewrite
ディレクティブ、またはパラメータをまったく使用しない場合、Nginxはリライトの結果に基づいて新しい一致する場所を検索します。
たとえば、最後の例を変更して書き換えを含めると、リクエストが2番目の場所に直接渡されることがあります。 try_files
指令:
root /var/www/main;
location / {
rewrite ^/rewriteme/(.*)$ /$1 last;
try_files $uri $uri.html $uri/ /fallback/index.html;
}
location /fallback {
root /var/www/another;
}
上記の例では、 /rewriteme/hello
最初は最初のロケーションブロックによって処理されます。 に書き換えられます /hello
場所が検索されます。 この場合、最初の場所と再び一致し、によって処理されます try_files
いつものように、多分キックバック /fallback/index.html
何も見つからない場合( try_files
上で説明した内部リダイレクト)。
ただし、 /rewriteme/fallback/hello
、最初のブロックが再び一致します。 書き換えが再度適用され、今回は結果として /fallback/hello
. その後、リクエストは2番目のロケーションブロックから処理されます。
関連する状況は、 return
送信時のディレクティブ 301
また 302
ステータスコード。 この場合の違いは、外部から見えるリダイレクトの形式でまったく新しいリクエストが発生することです。 これと同じ状況が発生する可能性があります rewrite
を使用する場合のディレクティブ redirect
また permanent
フラグ。 ただし、外部から見えるリダイレクトは常に新しいリクエストを生成するため、これらの場所の検索は予期しないものであってはなりません。
The error_page
ディレクティブは、によって作成されたものと同様の内部リダイレクトにつながる可能性があります try_files
. このディレクティブは、特定のステータスコードが検出されたときに何が発生するかを定義するために使用されます。 これは、次の場合には実行されない可能性があります try_files
そのディレクティブはリクエストのライフサイクル全体を処理するため、が設定されます。
この例を考えてみましょう。
root /var/www/main;
location / {
error_page 404 /another/whoops.html;
}
location /another {
root /var/www;
}
すべてのリクエスト(で始まるものを除く) /another
)は最初のブロックによって処理されます。最初のブロックはからファイルを提供します /var/www/main
. ただし、ファイルが見つからない場合(404ステータス)、内部リダイレクトは /another/whoops.html
が発生し、最終的に2番目のブロックに到達する新しい場所の検索につながります。 このファイルはから提供されます /var/www/another/whoops.html
.
ご覧のとおり、Nginxが新しい場所の検索をトリガーする状況を理解すると、リクエストを行うときに表示される動作を予測するのに役立ちます。
結論
Nginxがクライアントリクエストを処理する方法を理解すると、管理者としての仕事がはるかに簡単になります。 各クライアントリクエストに基づいて、Nginxが選択するサーバーブロックを知ることができます。 また、リクエストURIに基づいてロケーションブロックがどのように選択されるかを知ることもできます。 全体として、Nginxがさまざまなブロックを選択する方法を知っていると、各リクエストを処理するためにNginxが適用するコンテキストを追跡することができます。