Nginxサーバーブロックの理解:よくある設定の疑問

Nginxサーバーブロックの仕組みを理解しましょう。リクエストが正しいドメインにどのようにマッチングされるか、ブロック内に何を記述するか、マルチサイト設定を混乱なく整理する方法について解説します。

Nginxサーバーブロックの理解:よくある設定の疑問

Nginxサーバーブロックを理解することは、Nginxの設定をより明確にするための最速の方法の一つです。サーバーブロックは、どのサイトがリクエストを処理するか、どのドメイン名がマッチするか、どのファイルが提供されるか、そしてプロキシトラフィックの行き先を決定します。

複数のドメインをホストしている場合、HTTPをHTTPSにリダイレクトする場合、またはNginxの背後でアプリケーションを実行する場合、サーバーブロックがそのルーティングの大部分を担います。

Nginxサーバーブロックとは?

Nginxサーバーブロックは、特定のアドレス、ポート、ホスト名の組み合わせに対してNginxがどのように応答するかを定義する設定のセクションです。

基本的な静的サイトのサーバーブロックは次のようになります:

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com/public;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

これは、Nginxがポート80でリッスンし、example.comおよびwww.example.comへのリクエストをマッチングし、publicディレクトリからファイルを提供し、ファイルが見つからない場合は404を返すように指示します。

「サーバーブロック」という名前はNginxで一般的です。Apacheユーザーは同様の概念をバーチャルホストとして知っているかもしれません。目的は同じです:1つのウェブサーバーで複数のサイトやアプリケーションを処理できるようにすることです。

サーバーブロックは通常、DebianおよびUbuntuシステムでは/etc/nginx/sites-available/に保存され、/etc/nginx/sites-enabled/へのシンボリックリンクで有効化されます。一部のディストリビューションでは代わりに/etc/nginx/conf.d/を使用します。正確なレイアウトよりも、nginx.confにどのファイルがインクルードされているかを知ることが重要です。

Nginxはどのように正しいサーバーブロックを選択するのか?

Nginxの選択は段階的に行われます。まず、listenディレクティブからIPアドレスとポートを考慮します。次に、リクエストのホスト名をserver_nameと比較します。

ホスト名が一致しない場合、Nginxはそのアドレスとポートのデフォルトサーバーを使用します。明示的に指定することもできます:

server {
    listen 80 default_server;
    server_name _;
    return 444;
}

一部のチームは、このようなデフォルトブロックを使用して一致しないリクエストをドロップします。他のチームは単純な404を返します。最適な選択は、ロギングとセキュリティの好みによります。

学習中の場合は、可視性のある404デフォルトの方がreturn 444よりもデバッグが容易です。なぜなら、444はNginx固有の接続クローズであり、クライアント側からはネットワーク問題のように見える可能性があるからです。本番環境では、ランダムな一致しないホストに対して静かなクローズを好むチームもあります。チームが理解していれば、どちらの選択でも問題ありません。

正確な名前はワイルドカード名よりも優先されます。例えば、api.example.com*.example.comよりも具体的です。正規表現のサーバー名も可能ですが、読みづらくトラブルシューティングが難しいため、使用は稀であるべきです。

よくある間違いは、新しいドメインをDNSに追加したが、server_nameに追加するのを忘れることです。リクエストはサーバーに到達しますが、Nginxはそれをデフォルトブロックに送信します。ユーザーから見ると、間違ったサイトが表示されるか、リクエストが失敗します。

もう一つのよくある間違いは、同じlistenserver_nameを主張する2つのブロックを作成することです。Nginxは競合するサーバー名について警告し、そのうちの1つを無視する可能性があります。サイトを追加した後は常に設定をテストしてください。

間違ったサイトが応答する場合に使用するコマンド:

sudo nginx -T | grep -n "server_name"
curl -I -H 'Host: example.com' http://127.0.0.1/

nginx -Tは、インクルードされたファイルを含む完全にロードされた設定を出力します。これは、sites-enabledの下のすべてのファイルを開くよりも高速であることがよくあります。

サーバーブロック内には何を記述するのか?

サーバーブロックには通常、ドメインマッチング、TLS、ドキュメントルート、ロギング、リダイレクト、および1つ以上のlocationブロックに関するディレクティブが含まれます。

リバースプロキシの場合、ブロックは次のようになります:

server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:3000;
    }
}

ここでは、NginxがHTTPSを終端し、ローカルアプリケーション(ポート3000)にトラフィックを転送しています。これはNode.js、Python、Ruby、Go、Javaアプリケーションで一般的です。

アップストリームアプリが元のスキームやクライアントIPを知る必要がある場合、これらのproxy_set_header行が重要です。これらがないと、アプリはすべてのリクエストが127.0.0.1からプレーンHTTP経由で来たと認識する可能性があります。これにより、リダイレクト、監査ログ、レート制限、生成されたコールバックURLが壊れる可能性があります。

複数のサイトをホストする場合は、明確なログパスを使用してください:

access_log /var/log/nginx/app.example.com.access.log;
error_log /var/log/nginx/app.example.com.error.log;

ログを分離すると、トラブルシューティングがはるかに容易になります。すべてのサイトが1つのアクセスログに書き込む場合、どのドメインが失敗しているかを特定するのに時間がかかります。

忙しいホストの場合、これにより保持も容易になります。インシデント中はAPIの詳細なログが必要で、静的サイトでは軽いログで十分な場合があります。ファイルを分離しておけば、すべてのサーバーブロックを一度に変更することなく、そのオプションを利用できます。

サーバーブロックとロケーションブロックの違いは?

サーバーブロックはサイトを選択します。ロケーションブロックは、そのサイト内のパスに対して何を行うかを選択します。

例:

server {
    server_name example.com;

    location /assets/ {
        expires 30d;
    }

    location /api/ {
        proxy_pass http://api_backend;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

/assets/logo.pngへのリクエストは静的ファイルキャッシュを取得します。/api/usersへのリクエストはアップストリームバックエンドに送られます。その他のリクエストはフロントエンドアプリにフォールスルーします。

この分割は重要です。間違ったドメインが応答する場合は、サーバーブロックのマッチングを確認してください。正しいドメインが応答するが、間違ったパス動作が発生する場合は、ロケーションマッチングを確認してください。

この区別は時間を節約します。api.example.com/usersへのリクエストが失敗する理由は、api.example.comが間違ったサーバーブロックにマッチしたか、または正しいブロック内で/usersが間違ったロケーションにマッチしたかのいずれかです。これらは異なる問題です。

パスルーティングの詳細については、Nginxロケーションブロックの解説を参照してください。

複数のサイトをどのように整理すべきか?

可能な限り、サイトまたはアプリごとに1つのファイルを使用してください。各ファイルにドメインまたは目的に一致する名前を付けます:

/etc/nginx/sites-available/example.com
/etc/nginx/sites-available/api.example.com
/etc/nginx/sites-available/admin.example.com

これによりレビューが容易になり、誤ったサービスを変更する可能性が減ります。また、サイトを迅速に無効化する必要がある場合にも役立ちます。

共有スニペットは小さく、明確に保ちます。TLS設定、プロキシヘッダー、セキュリティヘッダーはインクルードに適した候補です。主要なルーティング動作を汎用的なインクルードファイルに隠すのは避けてください。デバッグが難しくなります。

実用的な設定には以下を含めることができます:

include snippets/proxy-headers.conf;
include snippets/security-headers.conf;

インクルードを使用して繰り返しを減らしますが、すべてのサイトを同一に動作させるためではありません。内部管理パネル、公開API、静的マーケティングサイトでは、異なる制限やヘッダーが必要になることがよくあります。

サーバーブロックファイルを削除または名前変更する前に、それがどのようにインクルードされているかを確認してください。Debianスタイルのレイアウトでは、sites-availableからファイルを削除しても、別のコピーがconf.dに存在する場合は何も起こりません。一方、sites-enabledのシンボリックリンクを削除すると、ソースファイルを削除せずにサイトが無効化されます。

ls -l /etc/nginx/sites-enabled/
sudo nginx -T | grep -n "include"

この簡単なチェックにより、正しいファイルを編集したと思ってもNginxが別のファイルを使用し続けるというイライラする状況を防げます。

サーバーブロックの問題で助けを求めるタイミング

サーバーブロックの変更が本番ドメイン、HTTPS証明書、顧客向けリダイレクト、または同じホスト上の複数のアプリケーションに影響を与える場合は、DevOpsエンジニアまたはNginxスペシャリストに助けを求めてください。小さなミスがユーザーを間違ったアプリに誘導したり、証明書の更新を妨げたりする可能性があります。

また、設定が正しいと思ってもNginxが間違ったサイトを提供し続ける場合も助けを求めるべきです。問題にはDNS、IPv6、ロードバランサー、CDN、または気づかなかったインクルードファイルが関与している可能性があります。

サーバーブロックは、Nginxがドメインレベルのトラフィックをルーティングするために使用するマップです。それらを具体的に保ち、変更ごとにテストし、別々のログを使用し、ドメインマッチングとパスルーティングを頭の中で分離してください。これが理解できれば、ほとんどのNginx設定の疑問ははるかに簡単に答えられるようになります。