NginxでHTTPをHTTPSにリダイレクトする:ベストプラクティス

専用のポート80ブロックを使用してNginxで信頼性の高いHTTPからHTTPSへのリダイレクトを設定し、リダイレクトループを回避し、適切なリダイレクトコードを選択し、準備ができたらHSTSを追加します。

NginxでHTTPをHTTPSにリダイレクトする:ベストプラクティス

NginxでHTTPをHTTPSにリダイレクトすることで、訪問者が古いhttp:// URLを入力したり、古いリンクをたどったりしても、暗号化されたバージョンのサイトを使用するようになります。クリーンなリダイレクト設定はセキュリティを向上させ、重複URLを回避し、ユーザーに一貫したエントリポイントを提供します。

最善のアプローチは通常シンプルです。ポート80はトラフィックをリダイレクトするためだけに開いたままにし、実際のサイトは有効なTLS証明書を使用してポート443で提供します。

リダイレクトはほぼ正しく設定するのが簡単であるため、詳細が重要です。パスを削除するリダイレクトはブックマークを壊します。間違ったホスト名を保持するリダイレクトは重複した正規URLを作成する可能性があります。ロードバランサーの背後にあるリダイレクトは、NginxがTLSがどこで終了するかを理解していないと、無限ループする可能性があります。

リダイレクトをパブリックAPIの一部と考えてください。人々はチャットでリンクを貼り付け、検索エンジンはそれらをクロールし、監視システムはそれらにヒットし、古いメールは何年もそれらを生かし続けます。リダイレクトが安定していれば、誰も気づきません。ずさんだと、アプリケーションがリクエストを受け取る前に、ユーザーは証明書の警告、壊れたパス、またはリダイレクトが多すぎるエラーを目にします。

HTTPSリダイレクトが重要な理由

HTTPSは、TLS暗号化を使用してブラウザとサーバー間のトラフィックを保護します。これがないと、ユーザーとサイト間のネットワークによってデータが検査または変更される可能性があります。これは、ログイン、フォーム、Cookie、管理エリア、API、および通常のブラウジングにも重要です。

リダイレクトは一貫性の維持にも役立ちます。検索エンジンとユーザーは、http://example.com/pagehttps://example.com/pageを2つの別々の宛先として見るべきではありません。恒久的なリダイレクトは、HTTPSが優先バージョンであることをクライアントに伝えます。

標準的なNginxパターンは、専用のポート80サーバーブロックです:

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

    return 301 https://$host$request_uri;
}

次に、HTTPSサーバーブロックが実際のサイトを処理します:

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

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

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

return 301ディレクティブは効率的で明確です。余分なロケーション処理を行わずに、Nginxに恒久的なリダイレクトを送信するように指示します。$request_uriはパスとクエリ文字列を保持するため、/docs?page=2https://example.com/docs?page=2になります。

完全なTLS設定については、NginxをHTTPSで保護するを参照してください。

ほとんどのサイトでは、ポート80ブロックにアプリケーションロジックを配置しないでください。静的ファイルを提供したり、アプリにプロキシしたり、多数のロケーションを含めたりするべきではありません。シンプルに保ちましょう:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

この小さなブロックは監査が容易で、HTTPS設定から逸脱する可能性が低くなります。

301リダイレクトと302リダイレクトの選択

本番サイトでは、HTTPからHTTPSへの恒久的なリダイレクトに301を使用します。ブラウザと検索エンジンは、HTTPS URLがHTTP URLを置き換えるべきであることを理解します。

リダイレクトが一時的な場合にのみ302または307を使用します。たとえば、証明書とホスト名が確定する前のテスト中に一時的なリダイレクトを使用する場合があります。HTTPSサイトの準備ができたら、恒久的なリダイレクトに切り替えます。

初期設定中は注意してください。ブラウザは301リダイレクトを積極的にキャッシュする可能性があります。誤って間違ったホスト名にリダイレクトすると、Nginxを修正した後でもブラウザが悪いリダイレクトを使い続ける可能性があります。可能な場合は、curl、プライベートブラウザウィンドウ、および非本番ホスト名でテストしてください。

実用的なデプロイフローは次のようになります:

  1. HTTPSサーバーブロックが直接機能することを確認します。
  2. 証明書がすべてのホスト名と一致することを確認します。
  3. HTTPリダイレクトサーバーブロックを追加します。
  4. いくつかのパスとクエリ文字列をテストします。
  5. 動作が正しい場合にのみ、一時的なリダイレクトを恒久的なリダイレクトに変更します。

また、正規のホスト名を決定する必要があります。example.comwww.example.comの両方が機能する場合は、優先されるパブリックホスト名として1つを選択します。そうしないと、ユーザーがホスト名間を行き来したり、検索エンジンが両方をインデックスしたりする可能性があります。

たとえば、すべてのHTTPトラフィックを非wwwのHTTPSホスト名にリダイレクトするには:

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

    return 301 https://example.com$request_uri;
}

これは、ユーザーがリクエストしたホストを保持するhttps://$host$request_uriとは異なります。

動作を明示的にしたい場合は、ホスト名リダイレクトとスキームリダイレクトを分割することもできます:

server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://example.com$request_uri;
}

これはより冗長ですが、最終的な宛先が明確になります。小規模なサイトでは、どちらのスタイルでも問題ありません。大規模なサイトで多くのエイリアスがある場合、明示的なサーバーブロックは後で変更する際の混乱を減らすことができます。

リダイレクトループと証明書の問題を回避する

リダイレクトループは、Nginx、ロードバランサー、またはアプリケーションが別のリダイレクトをトリガーするURLにリクエストを送り返し続けるときに発生します。これは、クラウドロードバランサーやCDNなど、TLSがNginxの前で終了する場合によく見られます。

シンプルな単一サーバー設定では、NginxはHTTPSを直接受信するため、リダイレクトは簡単です。プロキシチェーンでは、ユーザーがHTTPSで接続した場合でも、Nginxはロードバランサーから平文のHTTPを受信する可能性があります。アプリケーションがローカル接続スキームに基づいてHTTPSを強制しようとすると、ループが発生する可能性があります。

修正方法はアーキテクチャによって異なります。通常、ロードバランサーはX-Forwarded-Protoなどのヘッダーを渡し、アプリケーションまたはNginx設定は既知のプロキシアドレスからのみそれらを信頼する必要があります。

たとえば、Nginxが信頼できるロードバランサーの背後にあり、内部HTTPのみを受信する場合、NginxにすべてのローカルHTTPリクエストをリダイレクトさせたくないかもしれません。代わりに、ロードバランサーがパブリックHTTPからHTTPSへのリダイレクトを処理し、Nginxはプライベートネットワークからのトラフィックを提供します。Nginxが決定を下す必要がある場合は、前のプロキシからの信頼できる転送プロトコル情報が必要です。任意のインターネットクライアントからのX-Forwarded-Protoを信頼しないでください。

また、証明書がリダイレクトされるすべてのホスト名をカバーしていることを確認してください。ユーザーがhttp://www.example.comにアクセスし、https://www.example.comにリダイレクトする場合、証明書はwww.example.comに対して有効である必要があります。すべてをhttps://example.comにリダイレクトする場合、最終サイトの証明書はexample.comをカバーする必要があります。

以下でテストします:

curl -I http://example.com/some/path?x=1

次のような出力を探します:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/some/path?x=1

次にHTTPS URLをテストします:

curl -I https://example.com/some/path?x=1

HTTPS応答は、HTTPに戻す別のリダイレクトではなく、ページの実際のステータスを返す必要があります。

また、両方のホスト名が存在する場合はテストします:

curl -I http://www.example.com/
curl -I https://www.example.com/
curl -I http://example.com/
curl -I https://example.com/

短く予測可能なチェーンを探しています。HTTPから正規のHTTPS URLへの1つのリダイレクトが適切です。HTTP非wwwからHTTPS非www、HTTPS www、そして戻るなど、複数のホップがある場合は、Nginx、アプリ、CDNルール、またはDNSレベルの転送が互いに競合している兆候です。

チェーン全体を検査するには:

curl -IL http://www.example.com/some/path

-Lフラグはリダイレクトを追跡します。クリーンな設定では、出力は最初のHTTP応答と最終的なHTTPS応答を示すはずです。3つまたは4つのLocationヘッダーが表示される場合は、正規URLへの明確なルートが1つになるまでルールを簡素化します。

HSTSとその他のベストプラクティス

HTTPS設定が安定したら、HTTP Strict Transport Security(通常HSTSと呼ばれる)を検討できます。HSTSは、将来の訪問に対してブラウザに自動的にHTTPSを使用するように指示します。

一般的なヘッダーは次のようになります:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

これを軽率に追加しないでください。サブドメインを含める場合、すべてのサブドメインがHTTPSをサポートする必要があります。後でHTTPSを壊した場合、HSTSヘッダーを見たブラウザはHTTPバージョンへのアクセスを拒否する可能性があります。テスト中は短いmax-ageから始め、自信がついたら増やしてください。

その他のベストプラクティス:

  • ポート80ブロックをシンプルに保ちます。
  • 理由がない限り、パスとクエリ文字列を保持します。
  • 1つの正規ホスト名を選択します。
  • ブラウザだけでなくcurlでリダイレクトをテストします。
  • 証明書を自動的に更新し、更新の失敗を監視します。
  • 可能な場合は、アプリで複製する代わりに、リダイレクトロジックをNginxに保持します。

シンプルなリダイレクトルールは、推論が容易で、後でサイトを変更する際に壊れる可能性が低くなります。

よくある間違い

最も一般的な間違いは、単純なリダイレクトにrewriteを使用することです:

rewrite ^ https://example.com$request_uri permanent;

これでも機能しますが、return 301 ...の方が明確で、余分な書き換え処理を回避します。基本的なスキームリダイレクトではなく、本当にパターンマッチングが必要な場合にrewriteを使用してください。

もう1つの間違いは、$server_nameが何を含むかを理解せずにリダイレクトすることです。$hostはリクエストのホストヘッダーから取得されますが、$server_nameは一致したNginxサーバー名に基づいています。正規リダイレクトの場合、リテラルのホスト名が最も驚きの少ないオプションであることがよくあります:

return 301 https://example.com$request_uri;

また、証明書ツールがポート80でそれらを必要とする場合、ACME HTTPチャレンジパスをリダイレクトしないでください。多くのLet's Encryptセットアップはこれを自動的に処理しますが、カスタム設定では例外が必要になる場合があります:

location /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
}

location / {
    return 301 https://example.com$request_uri;
}

証明書クライアントがそれを使用する場合にのみ、その例外を追加してください。証明書がDNS検証またはツール管理の一時サーバーを通じて更新される場合は、リダイレクトブロックをシンプルに保ちます。

安全なロールアウトパターン

本番サイトの場合、段階的に変更を行います:

  1. HTTPSサーバーブロックがサイトを正しく提供することを確認します。
  2. 証明書の更新が機能するか、監視されていることを確認します。
  3. まだホスト名をテストしている場合は、ポート80に一時的なリダイレクトを追加します。
  4. curl -Icurl -ILで一般的なURLをテストします。
  5. リダイレクト先が確定したら301に切り替えます。
  6. 長期のHSTSを有効にする前に待機します。

この待機期間は便利です。忘れられたサブドメイン、古いwebhook URL、ハードコードされたhttp://リンク、または最初のテストマシンからは見えなかったCDNルールを発見する時間が得られます。

また、監視にも注意してください。アップタイムチェックがまだhttp://example.comを指している場合は、301を期待するか、リダイレクトを追跡して最終的なHTTPSページをチェックするかを決定する必要があります。どちらも有効ですが、モニターは実際に望む動作と一致する必要があります。

例:静的サイトとリバースプロキシ

静的サイトの場合、HTTPSブロックはドキュメントルートのみの場合があります:

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

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

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

Nginxの背後にあるアプリケーションの場合、リダイレクトブロックは同じままですが、HTTPSブロックはトラフィックをプロキシします:

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

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

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

重要なのは分離です。ポート80はブラウザがどこに行くべきかを決定します。ポート443はサイトを提供します。これらのジョブを混在させると、特に後で別のプロキシやCDNが追加された場合に、リダイレクト動作の推論が難しくなります。

編集後、常に以下を実行します:

sudo nginx -t
sudo systemctl reload nginx

テストが失敗した場合は、リロードしないでください。最初に構文エラーを修正し、再度テストします。

最後のチェックは、SSHでホスト上だけでなく、サーバーの外部から行う価値があります。ファイアウォール、CDN、またはロードバランサーは、実際のユーザーが見るものを変更する可能性があります。ラップトップ、監視場所、または一時的なクラウドインスタンスから同じcurl -Iチェックを実行します。外部の結果がlocalhostと異なる場合、リダイレクトの問題はおそらくNginxの前のネットワーク層にあり、サーバーブロック自体にはありません。動作する設定を書き換える前にそれを確認してください。

いつ助けを求めるか

サイトがCDN、クラウドロードバランサー、Kubernetes Ingress、または複数のリバースプロキシの背後にある場合は、DevOpsエンジニアに助けを求めてください。階層化されたインフラストラクチャでのHTTPSリダイレクトは、TLSがどこで終了し、どのヘッダーが信頼されるかに依存します。

また、多くのサブドメインにわたって長期のHSTSを有効にする前に助けを求めるべきです。間違った設定は、HTTPSの準備ができていないサービスからユーザーをロックアウトする可能性があります。

NginxでHTTPをHTTPSにリダイレクトすることは、小さな設定変更ですが、セキュリティに大きな影響を与えます。専用のポート80リダイレクトブロックを使用し、リクエストURIを保持し、証明書を確認し、ループをテストしてから完了と呼びましょう。