Nginxでは、サブドメインを維持しながらすべてのhttpリクエストをhttpsに書き換えるにはどうすればよいですか?


508

Webサーバー上のすべてのhttp要求をhttps要求に書き換えたいので、次のことから始めました。

サーバー{
    80を聞きます。

    場所/ {
      ^(。*)https://mysite.com$1パーマネントを書き換えます。
    }
...


1つの問題は、これによりサブドメイン情報(node1.mysite.com/folderなど)が削除されることです。すべてをhttpsに再ルーティングしてサブドメインを維持するために上記を書き換えるにはどうすればよいですか。


2
「受け入れられた回答」をserverfault.com/a/171238/90758に移動することを検討してください。それは正しいものです。
olafure

ハードコードされたmysite.comの代わりに$ server_nameを使用してください
Fedir RYKHTIK 14年

回答:


748

新しいバージョンのnginxでの正しい方法

この質問に対する私の最初の答えは特定の時間に正しかったことが判明しましたが、それは別の落とし穴になりました-最新の状態に保つには、課税書き換えの落とし穴を確認してください

私は多くのSEユーザーによって修正されたので、信用は彼らにありますが、より重要なことは、正しいコードです:

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

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000" always; 

       [....]
}

3
ただし、ドメインごとにこれを行う必要があります-いいえ?サーバー上のすべてのドメインに適用したい場合はどうしますか?
JM4

30
@ JM4:server_nameの代わりに$ host $を書き換えに使用し、listenディレクティブにdefault_serverを追加すると、サーバー上のすべてのドメインで機能します。
クラスファンシェルベン

5
301は有効期限なしでローカルキャッシュに保存されることに注意することが重要です。構成が変更されたときにあまり役に立たない
-Trefex

9
@everyone 307リダイレクトを使用して、POSTコンテンツを保持します。
マフムードアルクシ

10
サブドメインを使用している場合の$host代わりに使用する必要があることに注意してください$server_name
ナマズ

276

注:これを行うための最良の方法はhttps://serverfault.com/a/401632/3641によって提供されましたが、ここで繰り返します:

server {
    listen         80;
    return 301 https://$host$request_uri;
}

最も単純な場合、ホストは送信先のサービスに固定されます。これにより、ブラウザーへの301リダイレクトが行われ、ブラウザーのURLがそれに応じて更新されます。

以下は、正規表現のために非効率的な前の回答です。@ kmindiで示されているように、単純な301は素晴らしいです

私はnginx 0.8.39以上を使用しており、次を使用しました:

 server {
       listen 80;
       rewrite ^(.*) https://$host$1 permanent;
 }

クライアントに永続的なリダイレクトを送信します。


15
80にすべきだと思います-これはhttpをリッスンし、クライアントにhttps(443)として戻るように指示しているためです。
マイケルニール

3
これが一番の答えです!
ネイサン

3
これが最も負担の大きい答えです。
ケース

1
これは最も簡単な方法ですが、安全性は最も低くなります。この方法では、サーバーでの使用が許可されているかどうかを確認せずに、サーバーがユーザーを任意のページにリダイレクトできます。サーバーがmydomain.coにサービスを提供している場合、悪意のあるユーザーはサーバーを使用して、google.comなどのmydomain.coなどの他のドメインにユーザーをリダイレクトする可能性があります。
friedkiwi

10
@ cab0ltここにはセキュリティの問題はありません。リダイレクトを提供しても、セキュリティ上のリスクはありません。アクセス制御の要件がある場合は、ブラウザが新しいURLを要求した時点でそれらを確認する必要があります。ブラウザーはリダイレクトに基づいてアクセスするだけでなく、新しいURLを要求するためにリダイレクトを必要としません。
mc0e

125

最善かつ唯一の方法は、次のようにHTTP 301 Moved Permanentlyリダイレクトを使用することです。

server {
    listen         [::]:80;
    return 301 https://$host$request_uri;
}

恒久的移動HTTP 301すでに述べによれば、評価すべき正規表現が存在しないため、リダイレクトはまた、最も効率的であるpitfails


新しいHTTP 308 Moved PermanentlyはRequestメソッドを保持し、主要なブラウザでサポートされています。たとえば、を使用308すると、ブラウザはリダイレクトリクエストのリクエストメソッドをからPOSTに変更できなくなりますGET


ホスト名とサブドメイン保持する場合これが方法です。

DNSがない場合でも、ローカルで使用しているため、これは機能します。たとえばをリクエストしてhttp://192.168.0.100/index.phpおり、正確ににリダイレクトされますhttps://192.168.0.100/index.php

listen [::]:80ホストに使用するのは、にbindv6only設定しているためfalse、ipv4ソケットにもバインドするためです。listen 80IPv6が必要ない場合、または他の場所にバインドする場合に変更します。

Saif Bechanのソリューションserver_nameでは、私の場合はlocalhostを使用していますが、ネットワーク経由では到達できません。

Michael Nealeからの解決策は良いですが、pitfailsによると、リダイレクト301を使用したより良い解決策があります;)


引用してみますが、301はHTTPSでは機能しません。
ケース

5
何が機能しないのですか?記載されているサーバーセクションは、暗号化されていないhttp(sなし)トラフィックが暗号化されたサーバーに永続的にリダイレクトされることです(443(https)をリッスンするセクションはリストされていません)
-kmindi

これがhttpsとすべてでうまくいくことを確認しました-@kmindiあなたの答えを参照して私の答えを更新しました-私はそれが正しい方法だと思うので、これがポップアップし続けます!よくやった。
マイケルニール

ドメイン(非IP)リクエストを使用する場合、「[::]:80」を「80」に変更しない限り機能しません。
ジョセフラスト

:それは期待される動作可能性がありtrac.nginx.org/nginx/ticket/345。リッスンオプションを説明する回答を更新しました。
kmindi

20

サーバーブロック内では、次のこともできます。

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}

2
この構成では、私のサーバーはリダイレクトループを生成する原因
Corkscreewe

たぶんそこの場所で別のリダイレクトがありますまたはhttpsをあなたのサイト/アプリで使用可能にされていない原因
オリオール

1
これを除いて、他のどれも機能していないようでした。nginxバージョンの使用:nginx / 1.10.0(Ubuntu)
ThatGuy343

https:// $ host $ uriに賛成
AMB

4
これは、ロードバランサーの背後にいる場合の方法です!
アントワン

17

新しいサブドメインが常に作成されるため、上記は機能しませんでした。たとえば、約30のサブドメインのAAA.example.com BBB.example.com。

最後に、以下で動作する設定を取得しました:

server {
  listen 80;
  server_name _;
  rewrite ^ https://$host$request_uri? permanent;
}
server {
  listen  443;
  server_name example.com;
  ssl on;
  ssl_certificate /etc/ssl/certs/myssl.crt;
  ssl_certificate_key /etc/ssl/private/myssl.key;
  ssl_prefer_server_ciphers       on;
# ...
# rest of config here
# ...
}

ありがとうございました!nginxは、301 https://*/ここの他の回答で要求を途中で返すかキャンセルします。server_name _;$hostは、トリックをした答えでした。+1
ザムナッツ

1
これが最適です!ただし、一部のサーバーを_実際のドメインに置き換えることをお勧めします。たとえば、.domain.com2台のサーバーがあり、nginxが誤ってサーバーの1つをデフォルトサーバーにリダイレクトしていました。
zzz

1
これは私のために働いた唯一の答えです、ありがとう!
雪だるま

どうもありがとう。多くの解決策を試しましたが、うまくいきませんでした。このソリューションはすばらしく、私にとってはうまくいきました。サーバー名 _; これは何を意味しているのか分かりませんでした。これを説明してください。
パヴァンクマー

6

私は非常に重要な修正を加えてずっと前に正解にコメントを投稿しましたが、この修正を独自の回答で強調する必要があると感じています。セキュリティで保護されていないHTTPをセットアップし、ユーザーコンテンツを期待している場合、フォームを持っている場合、APIをホストしている場合、またはWebサイト、ツール、アプリケーション、またはユーティリティをサイトに話しかけるように構成している場合、以前の回答はどれも安全ではありません。

この問題はPOST、サーバーに要求が行われたときに発生します。サーバーの応答がプレーン30xリダイレクトである場合、POSTコンテンツは失われます。何が起こるかというと、ブラウザ/クライアントがSSLに要求をアップグレードするが、ということですダウングレードPOSTGET要求。POSTパラメータが失われ、正しくない要求は、サーバーに行われます。

解決策は簡単です。HTTP 1.1 307リダイレクトを使用する必要があります。これはRFC 7231 S6.4.7で詳しく説明されています。

  Note: This status code is similar to 302 (Found), except that it
  does not allow changing the request method from POST to GET.  This
  specification defines no equivalent counterpart for 301 (Moved
  Permanently) ([RFC7238], however, defines the status code 308
  (Permanent Redirect) for this purpose).

受け入れられたソリューションから適応したソリューションは307、リダイレクトコードで使用することです。

server {
       listen         80;
       server_name    my.domain.com;
       return         307 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000"; 

       [....]
}

4

私は次のようにそれをすることができました:

server {
listen 80;
listen 443 ssl;

server_name domain.tld www.domain.tld;

# global HTTP handler
if ($scheme = http) {
        return 301 https://www.domain.tld$request_uri;
}

# global non-WWW HTTPS handler
if ($http_host = domain.tld){
        return 303 https://www.domain.tld$request_uri;
}
}

https://stackoverflow.com/a/36777526/6076984


更新されたバージョン、IFなし:paste.debian.net/plain/899679
stamster

4

AWS ELBの背後でngnixを実行しています。ELBはhttpを介してngnixと通信しています。ELBにはリダイレクトをクライアントに送信する方法がないため、X-Forwarded-Protoヘッダーとリダイレクトを確認します。

if ($http_x_forwarded_proto != 'https') {
    return 301 "https://www.exampl.com";
}

1

あなたの場合はreturn 301 https://$host$request_uri;ポート80上のデフォルトの応答として、その後、サーバーは、遅かれ早かれ、オープンプロキシのリストに得ることができます[1]、インターネット上の他の場所でトラフィックを送信するために悪用される始めます。ログがこのようなメッセージでいっぱいになった場合、あなたはそれがあなたに起こったことを知っています:

42.232.104.114 - - [25/Mar/2018:04:50:49 +0000] "GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1" 301 185 "http://www.ioffer.com/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Hotbar 4.1.8.0; RogueCleaner; Alexa Toolbar)"

問題は、次の$hostように、ブラウザーがHostヘッダーで送信するものや、HTTPの開始行からのホスト名でさえもエコーバックすることです。

GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1

その問題のため、ここでの他のいくつかの答えはの$server_name代わりに使用することをお勧めします$host$server_name常にあなたserver_name宣言に入れたもの評価します。しかし、そこに複数のサブドメインがある場合、またはワイルドカードを使用する場合、宣言の後の最初のエントリ$server_nameのみを使用し、さらに重要なことにワイルドカードをエコーバックするだけなので、機能しません。server_name

それでは、セキュリティを維持しながら複数のドメインをサポートする方法は?自分のシステムでは、私がすることで、このジレンマに対処してきた最初の上場default_server使用していないブロックを$host、次に行い、ワイルドカードのブロックをリスト:

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

(2番目のブロックに複数のドメインをリストすることもできます。)

この組み合わせにより、example.com一致しないドメインはハードコーディングされた場所(常に)にリダイレクトされ、自分のドメインに一致するドメインは適切な場所に移動します。サーバーはオープンプロキシとしては役に立たないので、トラブルを引き付けることはありません。

もしあなたがおかしいと感じているなら、私はあなたのdefault_serverブロックがあなたの正当なドメインのどれにもマッチせ、不快な何かを提供することもできると思います。。。。

[1]技術的に「プロキシ」は間違った言葉です。サーバーが外に出ず、クライアントへのリクエストを実行せず、リダイレクトを送信するだけですが、正しい言葉が何であるかわかりません。また、目標が何であるかわかりませんが、ログがノイズでいっぱいになり、CPUと帯域幅を消費するため、停止することもできます。


0

誰も実際に100%正しいとは思わないようです。ポート80リクエストをWebサーバー全体で443相当に送信するには、server_nameディレクティブではなくlistenディレクティブを使用して、キャッチオールを指定する必要がありますhttps://nginx.org/en/docs/http/request_processing.htmlも参照してください

サーバー{
    80のデフォルトをリッスンします。
    listen [::]:80 default;
      return 307 https:// $ host $ request_uri;
}
  • $ hostはサブドメイン名をキャッチします。
  • 307および308には、POSTおよびGETの両方のリクエストURIが含まれます。
  • 307は一時的なもので、完全なテストの後、Permanent 308に変更されます。

また、default.confが既存のvhostを返すという問題が頻繁に発生するため、/ etc / nginx / conf.d /の内容を確認してください。nginxの問題を処理する私の順序は、常にデフォルトファイルを移動し、行ごとにコメントアウトして元に戻すことから始まります。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.