紹介
Nginxは世界で最も人気のあるウェブサーバーの1つです。多くの同時クライアント接続を持つ高負荷を正常に処理し、ウェブサーバー、メールサーバー、またはリバースプロキシサーバーとして機能することができます。
このガイドでは、Nginxがクライアントリクエストを処理する際にどのような裏側の詳細があるかについて説明します。これらのアイデアを理解することで、サーバーおよびロケーションブロックの設計における推測作業を排除し、リクエストの処理が予測不可能に思えなくなるかもしれません。
Nginxブロック設定
Nginxは、異なるコンテンツを提供するための設定を論理的にブロックに分割し、階層構造で配置します。クライアントリクエストが行われるたびに、Nginxはどの設定ブロックを使用してリクエストを処理するかを決定するプロセスを開始します。この決定プロセスについて、このガイドで説明します。
私たちが議論する主なブロックは、serverブロックとlocationブロックです。
A server block is a subset of Nginx’s configuration that defines a virtual server used to handle requests of a defined type. Administrators often configure multiple server blocks and decide which block should handle which connection based on the requested domain name, port, and IP address.
A location block lives within a server block and is used to define how Nginx should handle requests for different resources and URIs for the parent server. The URI space can be subdivided in whatever way the administrator likes using these blocks. It is an extremely flexible model.
Nginxがリクエストを処理するサーバーブロックをどのように決定するか
Nginxは管理者が複数のサーバーブロックを定義し、それらが別々の仮想ウェブサーバーインスタンスとして機能するように許可しているため、リクエストを満たすためにどのサーバーブロックが使用されるかを決定する手順が必要です。
これを行うために、最良のマッチを見つけるために使用される一連のチェックを定義されたシステムを通じて行います。このプロセス中にNginxが関心を持つ主要なサーバーブロックディレクティブは、listen
ディレクティブとserver_name
ディレクティブです。
listen
ディレクティブを解析して可能なマッチを見つける
まず、NginxはリクエストのIPアドレスとポートを見ます。これを各サーバーのlisten
ディレクティブと照合して、リクエストを解決できる可能性のあるサーバーブロックのリストを構築します。
listen
ディレクティブは通常、サーバーブロックが応答するIPアドレスとポートを定義します。デフォルトでは、listen
ディレクティブを含まない任意のサーバーブロックは、0.0.0.0:80
(またはNginxが通常のrootユーザーではなく実行されている場合は0.0.0.0:8080
)のリッスンパラメーターが与えられます。これにより、これらのブロックはポート80の任意のインターフェースでのリクエストに応答できますが、このデフォルト値はサーバー選択プロセス内であまり重要ではありません。
listen
ディレクティブは、次のように設定できます:
- IPアドレス/ポートの組み合わせ。
- A lone IP address which will then listen on the default port 80.
- A lone port which will listen to every interface on that port.
- Unixソケットへのパス。
最後のオプションは、通常、異なるサーバー間でリクエストを渡す場合にのみ影響を持ちます。
リクエストを送信するサーバーブロックを決定しようとするとき、Nginxはまず、listen
ディレクティブの具体性に基づいて次のルールを使用して決定しようとします:
- Nginxは、「不完全な」
listen
ディレクティブをすべて、欠落している値をデフォルト値で置換して変換し、各ブロックがそのIPアドレスとポートによって評価できるようにします。これらの変換の例をいくつか挙げます:listen
ディレクティブがないブロックは、0.0.0.0:80
の値を使用します。- IPアドレスが設定されていないポート
111.111.111.111
のブロックは、111.111.111.111:80
になります - IPアドレスが設定されていないポート
8888
のブロックは、0.0.0.0:8888
になります
- Nginxは、次に、IPアドレスとポートに基づいて、リクエストと最も特定的に一致するサーバーブロックのリストを収集しようとします。 これは、任意のインターフェースに一致させるために
0.0.0.0
を使用して機能的にそのIPアドレスを使用しているブロックは、特定のIPアドレスをリストする一致するブロックがある場合には選択されません。 いずれの場合でも、ポートは完全に一致する必要があります。 - 最も特定的な一致が1つだけの場合、そのサーバーブロックがリクエストを処理するために使用されます。 同じ特定度の複数のサーバーブロックが一致する場合、Nginxはその後、各サーバーブロックの
server_name
ディレクティブを評価し始めます。
listen
ディレクティブで同じ特定度のサーバーブロックを区別する必要がある場合にのみ、Nginxがserver_name
ディレクティブを評価する必要があることを理解することが重要です。 たとえば、example.com
が192.168.1.10
のポート80
でホストされている場合、example.com
のリクエストは常にこの例の最初のブロックで処理されます。2番目のブロックのserver_name
ディレクティブに関係なく。
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はまず、リクエストの
Host
ヘッダーの値とserver_name
が完全に一致するサーバーブロックを見つけようとします。これが見つかれば、関連するブロックがリクエストを処理するために使用されます。複数の完全一致が見つかる場合は、最初のものが使用されます。 - 完全一致が見つからない場合、Nginxは次に、先頭にワイルドカード(設定内の名前の先頭に
*
が付いていることを示す)を使用してserver_name
が一致するサーバーブロックを見つけようとします。一致するものが見つかれば、そのブロックがリクエストを処理するために使用されます。複数の一致が見つかる場合、最も長いマッチがリクエストの処理に使用されます。 - 先頭にワイルドカードを使用して一致するものが見つからない場合、Nginxは次に、末尾にワイルドカードを使用して
server_name
が一致するサーバーブロックを探します(設定内のサーバー名が*
で終わっていることを示す)。一致するものが見つかれば、そのブロックがリクエストを処理するために使用されます。複数の一致が見つかる場合、最も長いマッチがリクエストの処理に使用されます。 - 見つかったものがない場合、Nginxはトレーリングワイルドカードを使用して
server_name
を定義したサーバーブロックを評価します(名前の前に~
が付きます)。”Host”ヘッダーに一致する正規表現を持つ最初のserver_name
がリクエストを処理するために使用されます。 - 正規表現の一致が見つからない場合、NginxはそのIPアドレスとポートに対するデフォルトのサーバーブロックを選択します。
各IPアドレス/ポートの組み合わせには、上記の方法で動作が決定できない場合に使用されるデフォルトのサーバーブロックがあります。IPアドレス/ポートの組み合わせごとに、構成内の最初のブロックまたはlisten
ディレクティブの一部としてdefault_server
オプションを含むブロックがそれに該当します(これは最初に見つかったアルゴリズムを上書きします)。IPアドレス/ポートの組み合わせごとに、default_server
宣言は1つだけです。
例
リクエストのHost
ヘッダー値と完全に一致するserver_name
が定義されている場合、そのサーバーブロックがリクエストを処理するために選択されます。
この例では、リクエストの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アドレスとポートのデフォルトサーバーに渡されます。
一致する場所ブロック
Nginxがリクエストを処理するサーバーブロックを選択するプロセスに類似して、Nginxにはリクエストを処理するために使用するサーバー内の場所ブロックを決定するための確立されたアルゴリズムもあります。
場所ブロック構文
Nginxがリクエストを処理するためにどの場所ブロックを使用するかを決定する方法をカバーする前に、場所ブロック定義で見られる可能性のあるいくつかの構文について説明しましょう。場所ブロックは、サーバーブロック(または他の場所ブロック)内に存在し、リクエストURI(ドメイン名またはIPアドレス/ポートの後に来る部分)の処理方法を決定するために使用されます。
場所ブロックは、一般的に次の形式を取ります:
location optional_modifier location_match {
. . .
}
上記のlocation_match
では、NginxがリクエストURIをチェックするものが定義されています。上記の例に修飾子が存在するかしないかによって、Nginxが場所ブロックを一致させる方法が異なります。以下の修飾子は、関連する場所ブロックが次のように解釈される原因となります:
- (none): 修飾子が存在しない場合、場所はプレフィックスマッチとして解釈されます。これは、与えられた場所がリクエストURIの先頭と一致するかどうかを判定するために使用されます。
=
: 等号が使用される場合、このブロックはリクエストURIが場所と完全に一致する場合にマッチと見なされます。~
: タイルド修飾子が存在する場合、この場所は大文字小文字を区別する正規表現マッチとして解釈されます。~*
: タイルドとアスタリスク修飾子が使用されると、このロケーションブロックは大文字小文字を区別しない正規表現マッチとして解釈されます。^~
: キャレットとタイルド修飾子が存在し、このブロックが最適な非正規表現マッチとして選択された場合、正規表現マッチングは行われません。
ロケーションブロック構文のデモンストレーション例
プレフィックス一致の例として、次のロケーションブロックが選択され、/site
、/site/page1/index.html
、または /site/index.html
のようなリクエスト URI に応答します:
location /site {
. . .
}
正確なリクエスト URI 一致のデモンストレーションとして、このブロックは常に /page1
のようなリクエスト URI に応答するために使用されます。これは しない /page1/index.html
リクエスト URI に応答するために使用されます。このブロックが選択され、リクエストがインデックスページを使用して達成された場合、内部リダイレクトが行われてリクエストの実際のハンドラとなる別の場所にリダイレクトされます。
location = /page1 {
. . .
}
場所の例として、大文字と小文字を区別する正規表現として解釈されるべき場所のブロックを使用できます。このブロックは、/tortoise.jpg
のリクエストを処理しますが、/FLOWER.PNG
には対応しません。
location ~ \.(jpe?g|png|gif|ico)$ {
. . .
}
A block that would allow for case-insensitive matching similar to the above is shown below. Here, both /tortoise.jpg
and /FLOWER.PNG
could be handled by this block:
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 と一致する 最初の 正規表現の場所がすぐにリクエストの処理に使用されます。
- リクエスト URI と一致する正規表現の場所が見つからない場合、以前保存されたプレフィックスの場所がリクエストの処理に使用されます。
デフォルトで Nginx がプレフィックスの一致よりも正規表現の一致を優先して処理することが重要です。しかしながら、Nginx はまずプレフィックスの場所を 評価 するため、管理者は `=` や `^~` などの修飾子を使用して場所を指定することで、この傾向を上書きすることができます。
また、プレフィックスの場所が最も長く、最も具体的な一致に基づいて選択される一方、正規表現の評価は最初の一致する場所が見つかると停止されるため、正規表現の場所においては設定内の位置が非常に重要であることも覚えておくことが大切です。
最後に、Nginxが正規表現ロケーションを評価する際に、最も長いプレフィックス一致内の正規表現一致が「行を飛び越える」ことが重要であることを理解することが重要です。これらは、他の正規表現一致が考慮される前に、順番に評価されます。非常に有用なNginx開発者であるMaxim Douninは、この選択アルゴリズムのこの部分についてこの投稿で説明しています。
ロケーションブロックの評価が他のロケーションに移動するのはいつですか?
一般的に、リクエストを処理するために選択された場所ブロックが存在する場合、リクエストはそのコンテキスト内で完全に処理されます。選択された場所と継承されたディレクティブのみが、リクエストがどのように処理されるかを決定し、兄弟のロケーションブロックからの干渉はありません。
これは予測可能な方法でロケーションブロックを設計することを可能にする一般的なルールですが、選択された場所内の特定のディレクティブによって新しい場所検索がトリガーされる場合があることを認識することが重要です。 「1つの場所ブロックのみ」のルールの例外は、リクエストが実際にどのように処理されるかに影響を与える可能性があり、ロケーションブロックを設計する際の期待と一致しない場合があります。
この種の内部リダイレクトにつながるいくつかのディレクティブは次のとおりです:
- インデックス
- トライ_ファイル
- 書き換え
- エラー_ページ
これらについて簡単に説明しましょう。
コード>インデックス ディレクティブは、リクエストを処理するために使用される場合、常に内部リダイレクトにつながります。正確な場所の一致は、アルゴリズムの実行をすぐに終了し、選択プロセスを高速化するためによく使用されます。しかし、正確な場所の一致をディレクトリにした場合、リクエストが実際の処理のために別の場所にリダイレクトされる可能性が高いです。
この例では、最初の場所は/exact
のリクエストURIによって一致しますが、リクエストを処理するために、ブロックに継承されたインデックス
ディレクティブは2番目のブロックへの内部リダイレクトを開始します。
index index.html;
location = /exact {
. . .
}
location / {
. . .
}
上記の場合、実行が最初のブロックに留まる必要がある場合は、別の方法でディレクトリへのリクエストを満たす必要があります。たとえば、そのブロックの無効なインデックス
を設定し、autoindex
をオンにすることができます。
location = /exact {
index nothing_will_match;
autoindex on;
}
location / {
. . .
}
これはインデックス
がコンテキストを切り替えることを防ぐ1つの方法ですが、ほとんどの設定には役立たないかもしれません。ほとんどの場合、ディレクトリの正確な一致は、リクエストの書き換え(これにより新しい場所の検索が行われる)のようなことに役立ちます。
考慮する構成は次のとおりです:
上記の例では、/blahblah
へのリクエストが行われた場合、最初のロケーションが最初にリクエストを受け取ります。それは/var/www/main
ディレクトリ内のblahblah
という名前のファイルを探そうとします。見つけられない場合、blahblah.html
という名前のファイルを検索します。それから、/var/www/main
ディレクトリ内にblahblah/
というディレクトリがあるかどうかを確認しようとします。これらの試行がすべて失敗した場合、/fallback/index.html
にリダイレクトされます。これにより、2番目のロケーションブロックにキャッチされる別のロケーション検索がトリガーされます。これにより、/var/www/another/fallback/index.html
というファイルが提供されます。
root /var/www/main;
location / {
try_files $uri $uri.html $uri/ /fallback/index.html;
}
location /fallback {
root /var/www/another;
}
ロケーションブロックのパスオフにつながる別のディレクティブは、rewrite
ディレクティブです。 rewrite
ディレクティブにlast
パラメータを使用するか、パラメータを一切使用しない場合、Nginxはリライトの結果に基づいて新しい一致するロケーションを検索します。
たとえば、最後の例をリライトを含めて修正すると、try_files
ディレクティブに依存せずに、リクエストが時々直接2番目のロケーションに渡されることがあります:
上記の例では、/rewriteme/hello
へのリクエストは最初のロケーションブロックによって最初に処理されます。それは/hello
に書き換えられ、ロケーションが検索されます。この場合、再び最初のロケーションが一致し、通常通りtry_files
によって処理されます。何も見つからない場合(上記で説明したtry_files
の内部リダイレクトを使用して)、/fallback/index.html
に戻るかもしれません。
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/fallback/hello
に対して行われた場合、最初のブロックが再度一致します。再度リライトが適用され、今度は/fallback/hello
が結果として得られます。その後、リクエストは2番目のロケーションブロックから提供されます。
error_page
ディレクティブは、try_files
によって作成される内部リダイレクトに類似したものを生成する可能性があります。このディレクティブは、特定のステータスコードが遭遇されたときに何が起こるかを定義するために使用されます。try_files
が設定されている場合、おそらくこれは実行されないでしょう。なぜなら、そのディレクティブがリクエストのライフサイクル全体を処理するからです。
A related situation happens with the return
directive when sending the 301
or 302
status codes. The difference in this case is that it results in an entirely new request in the form of an externally visible redirect. This same situation can occur with the rewrite
directive when using the redirect
or permanent
flags. However, these location searches shouldn’t be unexpected, since externally visible redirects always result in a new request.
この例を考えてみましょう:
(/another
で始まるもの以外の)すべてのリクエストは、最初のブロックによって処理され、/var/www/main
からファイルが提供されます。ただし、ファイルが見つからない場合(404ステータス)、/another/whoops.html
への内部リダイレクトが発生し、最終的に2番目のブロックに到達します。このファイルは/var/www/another/whoops.html
から提供されます。
root /var/www/main;
location / {
error_page 404 /another/whoops.html;
}
location /another {
root /var/www;
}
ご覧の通り、Nginxが新しいロケーション検索をトリガーする状況を理解することは、リクエストを行った際の振る舞いを予測するのに役立ちます。
ご覧の通り、Nginxが新しい場所の検索をトリガーする状況を理解することで、リクエストを行った際に見られる動作を予測することができます。
結論
Nginxがクライアントのリクエストを処理する方法を理解することで、管理者としてのお仕事がはるかに楽になります。Nginxが各クライアントのリクエストに基づいてどのサーバーブロックを選択するかを知ることができます。また、リクエストURIに基づいて場所ブロックがどのように選択されるかを説明することもできます。全体として、Nginxが異なるブロックを選択する方法を知ることで、Nginxが各リクエストをサービスするために適用するコンテキストを追跡する能力を与えられます。