ブログに戻る

Nginx サーバーおよび Location ブロック選択アルゴリズム:概要

Nginx サーバーおよび Location ブロック選択アルゴリズム:概要

はじめに

Nginxは、世界で最も人気のあるWebサーバーの選択肢の1つです。多数の同時クライアント接続を正常に処理することができます。同時に、メール、Web、またはリバースプロキシサーバーとしても機能します。

このガイドは、Nginxがクライアントのリクエストを処理する方法を制御する舞台裏の仕組みを概説することを目的としています。サーバーとlocationのブロック設計を解き明かし、リクエスト処理の一見予測不可能な動作を最適に軽減する方法を説明します。

まず最初に、以下はUbuntuサーバーにNginxをインストールする方法に関する包括的なチュートリアルです。それでは、始めましょう!

Nginxによるブロック設定

Nginxの論理的なアプローチには、異なる目的のための設定を、より論理的な別々のコンテンツブロックに分類することが含まれます。これらは階層構造で配置されます。クライアントがリクエストを生成すると、Nginxはこれらの設定ブロックのうち、どれがそのリクエストの処理に最も適しているかを決定するプロセスを開始します。ここでは、この決定プロセスに焦点を当てます。

ここで説明する主なブロックは、serverブロックとlocationブロックです。Serverブロックは、定義されたタイプのリクエストの処理をどの仮想サーバーが担当するかを定義する、Nginxが確立する設定のサブセットです。これらは通常、受信リクエストのIPアドレス、ドメイン名、またはポートに基づいています。管理者は複数のserverブロックを設定します。そして、どの接続がリクエストを処理すべきかを決定する必要があります。

Locationブロックはserverブロック内に配置されます。これらは、特定の親サーバーへの受信リクエストを処理するために、どのように、どのリソースを活用できるかを決定するものです。このモデルは非常に柔軟です。URIスペースは、管理者が最適と考える方法でこれらのブロックを使用するように設定できます。

Nginxでどのブロックがどのリクエストを処理するかを決定する

Nginxでは、複数のserverブロックを定義できます。これらはすべて、異なる仮想Webサーバーとして機能します。したがって、特定の受信リクエストをどのサーバーが処理するかを決定する方法が必要です。これは、定義されたチェックシステムを通じて、リクエストの処理に最適なものを特定することによって行われます。

Nginxは主に、2つの主要なserverブロックディレクティブを扱います:listenserver_name.

「Listen」ディレクティブによる一致候補の検索

Nginxが最初に評価するのは、リクエストのポートとIPアドレスです。次に、それを各サーバーのlistenディレクティブと照合します。このサーバーリストの解析により、該当するリクエストを解決できるserverブロックだけを絞り込むことができます。

通常、listenディレクティブは、特定のserverブロックが応答を担当するポートとIPアドレスを定義します。listenディレクティブを持たないserverブロックは、デフォルトで0.0.0.0:80のlistenパラメータを受け取ります。Nginxが通常の非rootユーザーによって実行されている場合、listenパラメータは0.0.0.0:8080として定義されます。つまり、インターフェースが何であれ、ブロックがポート80から来ている場合、そのように定義されたブロックがそれに応答します。ただし、このデフォルト値は、サーバーを選択するプロセスにおいてそれほど重視されません。

listenディレクティブは以下のように設定できます:

  • デフォルトポート(80)でリクエストをリッスンする単一のIPアドレス。
  • そのポート上の任意のインターフェースをリッスンする単一のポート。
  • ポートとIPアドレスの組み合わせ。
  • 設定されたUnixソケットパス(このオプションは、リクエストが異なるサーバー間を通過する場合にのみ影響します)。

Nginxは、どのserverブロックにリクエストを送信するかを決定する際に、一連のルールを適用します。ルールは、listenディレクティブの特定の構成によって異なります。それらは以下の通りです:

  • listenディレクティブが不完全な場合、欠落している部分にはデフォルト値が適用されます。これは、リクエストを処理するために、IPアドレスとポートがデフォルト値で強制的に補完されることを意味します。
    • この場合、listenディレクティブを含まないブロックは、デフォルト値の0.0.0.0:80を使用します。
    • ポートが欠落しており、IPアドレスが111.111.111.111であるブロックは、111.111.111.111:80になります。
    • IPアドレスがない場合、ポート8888を持つブロックはデフォルトのIPアドレスを取得して追加し、0.0.0.0:8888を作成します。
  • 決定されたIPアドレスとポートを使用して、Nginxはそのポートに一致するものとして提供されているサーバーブロックを探します。
  • 特定の一致が1つだけ見つかった場合、それが該当するサーバーブロックになります。条件を満たすブロックが複数ある場合、Nginxはserver_nameディレクティブを参照して、問題の正確なサーバーブロックをさらに絞り込みます。

Nginxは、listenディレクティブから正確な特定レベルを持つサーバーブロックが見つからなかった場合にのみ、server_nameディレクティブの評価を行います。example.comがポート80にあり、IPが192.168.1.10である場合、この例の最初のブロックが常にリクエストを処理するブロックになります。これは、server_nameディレクティブの内容に関係なく当てはまります。

Nginx Server

特定度の一致を伴う、条件を満たすサービスブロックが複数存在する場合、server_nameディレクティブが考慮されます。

「Server_Name」ディレクティブで一致する可能性のあるものを探す

listenディレクティブの特定度が同じである場合、Nginxはリクエスト’の「Host」ヘッダーを確認します。これは、クライアントが最初にアクセスしようとしていたドメインのIPを持つ値です。Nginxは、候補として残っている各サーバーブロック内のserver_nameディレクティブを利用します。これらの評価は、以下のルールに基づいて行われます。

  • Nginxの最初の試みは、リクエストの「Host」ヘッダー値と完全に一致するserver_nameを持つブロックを特定することです。それが見つかった場合、完全一致するブロックがリクエストを処理するブロックになります。複数のブロックが見つかった場合は、リストの最初のブロックが選択されます。
  • 完全一致がない場合、Nginxはserver_nameを使用して、設定内のサーバーブロック名の先頭にあるワイルドカード「*」を使用して一致するサーバーブロックを探します。この方法で見つかった場合、そのサーバーブロックに決定されます。複数の一致が見つかった場合は、最も長い一致がリクエストを処理するものになります。
  • 先頭のワイルドカードが一致しない場合、Nginxは末尾のワイルドカードが一致するサーバーブロックを探します。つまり、設定内で末尾に「*」があるサーバー名になります。見つかった場合は、それがリクエストに使用されます。一方、複数見つかった場合、Nginxは再び最も長い一致を使用します。
  • 両方のワイルドカードの試行後も一致するものがない場合、Nginxは正規表現(名前の前の「~」で指定)を使用してserver_nameを定義しているサーバーブロックを評価します。「Host」ヘッダーと一致する表現を持つ最初のserver_nameのインスタンスが、リクエスト処理用のサーバーブロックとみなされます。
  • この時点でも一致するものがない場合、NginxはそのポートとIPアドレスの組み合わせに対するデフォルトのサーバーブロックを使用します。

すべてのポートとIPアドレスの組み合わせには、指定されたサーバーブロックが存在します。リクエスト処理に適したサーバーブロックを決定するルールが実を結ばなかった場合、これが使用されます。これは、listenディレクティブにdefault_serverオプションが含まれている設定内の最初のブロックになります(最初に検出されたアルゴリズムを上書きします)。各IPアドレスとポートの組み合わせに設定できるdefault_serverは、最大で1つだけです。

サーバーブロック選択の例

定義されたserver_nameが「Host」ヘッダー値と完全に一致する場合、それがリクエスト処理用に選択されるサーバーブロックになります。次の例は、リクエストの「Host」ヘッダーが「host1.example.com」として指定されている場合を示しています。この場合、2番目のサーバーが選択されます。

Nginx Server

完全一致がない場合、Nginxはワイルドカード付きのserver_nameが存在するかどうかを確認します。存在しない場合は、ワイルドカードで始まる最も長い一致が選択されます。以下では、「www.example.org」が「Host」ヘッダーにあります。つまり、2番目のブロックが選択されます。

server screenshot

ワイルドカードで始まる一致がない場合、Nginxは末尾のワイルドカードのチェックに進みます。リクエスト処理には、ワイルドカードで終わる最長の一致が選択されます。この例では、“Host”ヘッダーは「www.example.com」であるため、3番目のサーバーブロックが選択されます:

Nginx Server

それでも一致するものがない場合、Nginxは正規表現を使用してserver_nameディレクティブとのマッチングを試みます。それらの正規表現の最初のものがリクエスト処理のために選択されます。“Host”が「www.example.com」である場合、リクエストを処理するために2番目のサーバーブロックが選択されます:

Nginx Server

それでも一致するものがない場合、リクエストは、一致するデフォルトサーバーが設定されているIPアドレスとポートの組み合わせに送られます。

Locationブロックのマッチング

Nginxは、サーバー上のどのlocationブロックがリクエストへの応答を担当するかを決定するためのアルゴリズムを確立する必要もあります。

Locationブロックの構文

Nginxがリクエストを処理するlocationブロックをどのように決定するかを説明する前に、locationブロック定義の構文をおさらいしておきましょう。前述のように、locationブロックはserverブロック(および他のlocationブロック)の内部に配置されます。その目的は、リクエストURIをどのように処理するかを決定することです。URIは、リクエスト内のIPアドレスとポート、またはドメイン名の後に続く部分です。

Locationブロックは通常、次のようになります。

Syntax for Location Blocks

Nginxは、リクエストのURIをlocation_matchと照合します。上記の修飾子が存在するかどうかによって、Nginxがブロックのマッチングを試みる方法が決まります。修飾子に応じて、locationブロックは以下のルールに従って解釈されます。

  • 修飾子なし: 修飾子がない場合、locationは前方一致(プレフィックス一致)として解釈されます。これは、指定されたlocationがリクエストのURIの先頭部分と一致するかどうかを判定して、正しい一致を決定することを意味します。
  • =: 等号(=)は、リクエストのURIが指定されたlocationと完全に一致する場合にのみ、このブロックが一致とみなされることを示します。
  • ~: チルダ(~)修飾子は、locationブロックのマッチングで大文字と小文字が区別されることを表します。
  • ~*: チルダとアスタリスクの組み合わせ(~*)修飾子は、locationブロックのマッチングで大文字と小文字を区別しないことを表します。
  • ^~: チルダ修飾子の前にキャレット(^~)がある場合、このブロックが最適な非正規表現一致として選択されている限り、正規表現のマッチングは行われません。

Locationブロックの構文例

前方一致の例を示すと、次のlocationブロックは、/site、/site/page1/index.html、または/site/index/htmlという形式のリクエストURIに応答するために選択されます。

location site

この必須URIマッチングのデモンストレーションにおいて、このブロックは常に/page1という形式のURIリクエストに応答するために使用され、/page1/index.htmlというリクエストURIには使用されません。これが選択されたブロックであり、インデックスページを使用してリクエストを処理する場合、リクエストの実際のハンドラーは内部的に別のlocationにリダイレクトされます。

Nginx Server

例えば、大文字と小文字を区別する表現として解釈される必要があるlocationの場合、次のブロックは/FLOWER.PNGへのリクエストを処理できません。ただし、/tortoise.jpgへのリクエストは処理します。

Nginx Server and Location Block Selection Algorithms: Overview

次に、上記と同様に大文字と小文字を区別しないマッチングを許可するブロックを見てみましょう。この場合、このブロックは //tortoise.jpg /FLOWER.PNG の両方を処理できます:

location

最後のバリアントは、最適な非正規表現一致であると判断された場合に、正規表現のマッチングが行われないようにするブロックです。これは/costumes/ninja.htmlへのリクエストを処理できます。

Nginx Server and Location Block Selection Algorithms: Overview

はっきり言えば、修飾子はlocationブロックがどのように決定されるかを規定します。しかし、これだけでは、リクエストの送信先となるlocationブロックを特定するためにNginxがどのような意思決定アルゴリズムを使用しているかはわかりません。次にこれについて説明します。

Nginxがリクエストを処理するLocationの選択

Nginxがリクエストを処理するロケーションを選択する方法は、サーバーブロックが選択される方法と似ています。言い換えれば、プロセスを実行することによって、すべてのリクエストに対して最適なロケーションを決定します。Nginxを正確かつ適切に設定するには、このプロセスを理解することが不可欠です。

先ほど説明したロケーション宣言を念頭に置いて、Nginxは同様に、指定されたリクエストからのURIを比較することにより、すべてのロケーションの適格性をチェックして、潜在的なロケーションコンテキストを使用します。これにおいて、以下のアルゴリズムを適用します。

  • まず、Nginxは正規表現を含まないすべてのロケーションタイプをチェックします。これは、すべてのロケーションベースのプレフィックス一致を探すことによって行われます。そのために、リクエストの完全なURIに対してロケーションをチェックします。
  • Nginxは完全一致の検索を開始します。= 修飾子を使用しているロケーションブロックが特定されると、それがURIリクエストと比較されます。両者が完全に一致した場合、そのロケーションブロックがその場でリクエストを処理するために選択されます。
  • = 修飾子の比較に完全に一致するロケーションがない場合、Nginxは完全一致ではないプレフィックスの評価に進みます。リクエストのURIに一致する最長プレフィックスのロケーションを決定すると、以下の評価を実行します。
    • 最長プレフィックス一致のロケーションが ^~ 修飾子を使用している場合、このロケーションが即座に選択されます。
    • 最長プレフィックスのロケーションが ^~ 修飾子を使用していない場合、検索の焦点を移行できるように、その一致はNginxによって一時的に保持されます。
  • 最長プレフィックスロケーションの一致が見つかって保存されると、Nginxは正規表現ロケーションの評価に移行します。これらには、大文字と小文字を区別する一致と区別しない一致の両方が含まれます。最長一致プレフィックスロケーション内に正規表現ロケーションがある場合、Nginxはリストを再構成して、これらをロケーションリストの上部付近に配置します。再ソートされたリストからリクエストのURIに一致する最初の式が、リクエストを処理するために選択されるロケーションになります。
  • リクエストRIを満たす正規表現が見つからない場合、以前に保存されたロケーションがリクエストを処理するために選択されます。

Nginxはデフォルトで、優先的なプレフィックス一致よりも正規表現の一致を優先します。ただし、管理側が = および ^~ 修飾子を使用してこの傾向を上書きできるように、最初にプレフィックスロケーションを評価します。

もう1つの重要なポイントは、プレフィックスロケーションは通常、最も具体的で最長の一致に基づいているのに対し、正規表現のチェックは最初の一致が特定されるとすぐに停止されるということです。これは、設定内での配置が正規表現ロケーションに実際の影響を与えることを意味します。

最後に触れておくべき点として、最長プレフィックスを持つ一致内の正規表現一致は、Nginxのロケーション評価中に実質的に優先されます。これらはリストの最上部に配置され、他の正規表現よりも先に評価されます。

ロケーションブロックの評価において、他のロケーションへのジャンプはいつ発生しますか?

通常、リクエストが評価され、それを処理するロケーションブロックが選択されると、そのコンテキスト内のみで完全に処理されます。これは、兄弟ロケーションブロックからの入力なしに、継承されたディレクティブと選択されたロケーションのみがリクエストの処理における決定要因になることを意味します。

これはロケーションブロックの予測可能な設計を可能にする一般的な原則ですが、ロケーション内の特定のディレクティブが新しい検索をトリガーすることもあります。言い換えれば、「1つのロケーションブロックのみ」というルールにはいくつかの例外があります。これらの例外は、ロケーションブロックの期待と一致しない場合があります。したがって、リクエストが期待通りに処理されない可能性があります。

これらの内部リダイレクトは、以下を含むいくつかのディレクティブによって発生する可能性があります。

  • index
  • rewrite
  • error_page
  • try_files

indexディレクティブを使用すると、リクエスト処理中に常に内部リダイレクトが発生します。通常、ロケーションの一致が見つかると、選択プロセスを高速化するためにアルゴリズムの実行が終了しますが、見つかったロケーションの一致がディレクトリである場合、リクエストは正式に処理されるために別のロケーションにリダイレクトされる可能性が高くなります。

例えば、次の最初のロケーションはリクエストURIの /exact と一致します。しかし、リクエストを処理するために、ロケーションブロックが継承するindexディレクティブは、リクエストをセカンダリブロックにリダイレクトします。

index

そのシナリオにおいて、実行をプライマリブロック内に留める必要がある場合は、別のスキームでディレクトリへのリクエストを処理する必要があります。これを行う1つの方法は、該当するブロックに無効なインデックスを設定し、代わりにオートインデックスを有効にすることです。

location exact

この方法はいくつかのケースで機能するかもしれませんが、ほとんどの文脈において概して実用的ではありません。正確なディレクトリ一致は、リクエストを書き換える必要がある状況で役立ちます。これにより、まったく新しいロケーション検索がトリガーされます。

処理ロケーションを再評価するために使用できるもう1つのディレクティブは、try_filesディレクティブです。これはNginxに対し、指定された一連のファイルまたはディレクトリが存在するかどうかを具体的に確認するよう指示し、最後の検索基準はNginxが内部的にリダイレクトするURIになります。

次の設定について考えてみましょう。

root var

/blahblah へのリクエストがある場合、最初のロケーションがそれを受信します。/var/www/main ディレクトリに blahblah ファイルが見つからない場合、blahblah.html の追跡検索がトリガーされます。次に、/var/www/main ディレクトリ内の blahblah という名前のサブディレクトリを探します。これらすべてのチェックが失敗した場合、/fallback/index.html にリダイレクトされます。これにより、別のロケーションブロックが捕捉する別のロケーション検索がトリガーされます。その後、ファイル /var/www/another/fallback/index.html を処理します。

別のロケーションブロックへのリダイレクトをもたらすもう1つのディレクティブは、rewriteディレクティブです。lastパラメータが使用されている場合、Nginxはrewriteディレクティブの結果に基づいて新しい一致するロケーションを検索します。最後の例を変更してこのrewriteディレクティブを含めると、try_filesディレクティブを実装しなくても、リクエストを別のロケーションにリダイレクトできることが明らかになります。

root var ww main

この例では、/rewrite/hello へのリクエストは、最初は最初のロケーションによって処理されます。/hello に書き換えられた後、セカンダリロケーション検索がトリガーされます。これは最初のロケーションと一致します。これはtry_fileディレクティブによって処理され、ヒットしない場合は /fallback/index.html に戻る可能性があります。

しかし、/rewrite/fallback/hello へのリクエストが行われた場合、最初のブロックへの一致が見つかります。したがって、書き換えが再度処理されますが、今回は結果として /fallback/hello が返されます。リクエストは別のロケーションブロックで処理されます。

returnディレクティブを使用して301または302ステータスコードを送信する場合も、同様の状況が発生します。唯一の違いは、新しいリクエストが発生し、非常に明白なリダイレクトとして現れることです。同様に、permanentまたはredirectフラグを適用した場合、rewriteディレクティブでもこれが発生する可能性があります。

try_againと同様の内部リダイレクトを引き起こす可能性のあるもう1つのディレクティブは、error_pageディレクティブです。これは、処理中に特定のエラーコードが発生した場合に使用できます。try_filesディレクティブが設定されている場合、error_pageディレクティブは実行されない可能性が高くなります。それは、そのディレクティブがリクエストの全ライフサイクルを処理するためです。

次の例を考えてみましょう。

root screenshot

この場合、すべてのリクエストは /var/www/main からファイルを提供する最初のブロックによって処理されます。これは、/another で始まるリクエストには適用されません。しかし、ファイルが見つからなかった場合、/another/whoops/html への内部リダイレクトが開始されます。これにより、別のロケーション検索が発生します。結果として、リクエストはセカンダリブロックに送られ、そのファイルは /var/www/another/whoops.html から処理されます。

明らかなように、Nginxが新しいロケーション検索をトリガーする状況を理解することは、リクエストの処理時におけるシステム動作をより正確に予測するのに役立ちます。

結論

管理者がNginxによるクライアントリクエストの処理方法を理解すると、その業務は非常にシンプルになります。これにより、管理者はリクエストがどのサーバーブロックに送られるかを特定できるようになります。また、リクエストURIに基づいて選択されるロケーションブロックを判断することもできます。全体として、各リクエストを処理する際にNginxが適用するコンテキストを追跡する能力も管理者に提供されます。

最後に、当ブログのNginxに関する他のチュートリアルもご覧いただけます。これらは、世界で最も人気のあるWebサーバーの1つをより効果的に活用するのに役立ちます:

ハッピーコンピューティング!

author

Manpreet Singh

著者 · CloudSigma

Preslav DobrevはCloudSigmaのクリエイティブデザイナーであり、従来型および革新的なマーケティングチャネルを活用した一貫性のあるビジネスアイデンティティに注力しています。彼は芸術的なビジョンと戦略的マーケティングを融合させ、インパクトのあるブランドナラティブを生み出すことに長けています。

コメント

コメントはまだありません。最初のコメントを投稿しましょう。