nginxでSSLを強制またはリダイレクトする方法は?


222

次のようなサブドメインにサインアップページがあります。 https://signup.example.com

HTTPSを介してのみアクセスできるようにする必要がありますが、人々はHTTPを介して何らかの方法でつまずいて404を取得するのではないかと心配しています。

nginxの私のhtml / serverブロックは次のようになります。

html {
  server {
    listen 443;
    server_name signup.example.com;

    ssl                        on;
    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    ssl_session_timeout 30m;

    location / {
      root /path/to/my/rails/app/public;
      index index.html;
        passenger_enabled on;
    }
  }
}

何を追加して、http://signup.example.comリダイレクト先に行く人たちに追加できhttps://signup.example.comますか?(FYI強制できるRailsプラグインがあることは知っていますが、それSSLを避けたいと思っていました)


回答:


145

nginxの落とし穴によると、$request_uri代わりに使用して、不要なキャプチャを省略する方がわずかに良いです。その場合、疑問符を追加して、nginxがクエリ引数を2倍にしないようにします。

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}

68
または、リンクしたサイトによると、「より良い」return 301 http://domain.com$request_uri;
nh2

13
1つのコメント。$ server_name $は最初のserver_name変数を取得します。あなたの設定で非FQN名を持っているのであればこれを意識する
engineerDave

2
@ nh2これは、ドキュメントの誤りの別のケースです。なぜなら、使用するreturn 301...と、rewriteメソッドが実際に機能しているときに「too many redirects」エラーが発生するからです。
マイクベサニー

1
これは、「また悪い」として文書化されています。@MikeBethanyはreturn 301(私は推測)しない限り、あなたはそれをトリガーしている、仕事をして(両方のポートに設定する例を聞くことによって、正しいURLの問題をトリガー:。取るserverfault.com/a/474345/29689's最初の答えをしている場合は省略)。
ブレイザーブレード

1
私は長年にわたって変更されているのだろうと、この他の答えは優れているかどうか:serverfault.com/a/337893/119666
ライアン

256

公式のハウツーで説明されている最良の方法は、returnディレクティブを使用することです。

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

5
最短回答と私の場合
mateusz.fiolka

1
これは一般的に推奨されます。これは、301 Moved Permanently(リンクが永続的に移動された)を返すだけでなく、再書き込み
sgb 14

1
それはあなたが設定している場合でも、「あまりにも多くのリダイレクト」エラーが発生するので、これは動作しませんproxy_set_header X-Forwarded-Proto https;
マイクベサニ

1
@MikeBethanyはlisten 443;同じブロックで定義していますか?
ジョーB

2
これは受け入れられた答えでなければなりません。
sjas

119

すべてを1つのサーバーブロックに保持する場合は、これが正しい最も効率的な方法です。

server {
    listen   80;
    listen   [::]:80;
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

上記の「rewrite」や「​​if ssl_protocol」などを使用すると、すべてが遅くなります。

これは同じですが、httpプロトコルで書き換えを実行するだけで、すべてのリクエストで$ scheme変数をチェックする必要がなくなります。しかし、真剣に、それはあなたがそれらを分離する必要がないほど小さいことです。

server {
    listen   80;
    listen   [::]:80;

    server_name www.example.com;

    return 301 https://$server_name$request_uri;
}
server {
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;
}

8
この答えは正しいのに、何人かのco病者が理由を言わずにこの答えに賛成票を投じました。たぶん、それらの「もし悪なら」カルトのもう一人。IfについてNginxのドキュメントを読むのが面倒なら、IfIsNOTEvil、CERTAINがlocation {}コンテキスト内でそれを使用していることがわかります。私の答えは絶対に正しいやり方です!
DELETEDACC

2
私はこれに賛成票を投じませんでしたが、最新バージョンではデフォルトが「default_server」に変更されたことを指摘したいと思います。
スパーダー

2番目のソリューションがさらに効率的である場合、最初のソリューションは最も効率的ではありません。また、ifを使用しない理由についても説明しました。「リクエストごとに$ scheme変数をチェックする必要がありません」。ifsを使用しないことのポイントは、パフォーマンスだけでなく、宣言的であり、必須ではないことでもあります。
pepkin88

if($ scheme = http)の+1
フェルナンドコシュ

他の回答で述べたように、ここで$ hostを使用する必要があります。
アルテムルサコフスキー

56

新しいデュアルHTTPおよびHTTPSサーバー定義を使用している場合、次を使用できます。

server {
    listen   80;
    listen   [::]:80;
    listen   443 default ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($ssl_protocol = "") {
       rewrite ^   https://$server_name$request_uri? permanent;
    }
}

これは私にはうまくいくようで、リダイレクトループは発生しません。

編集:

置換:

rewrite ^/(.*) https://$server_name/$1 permanent;

Pratikの書き換えラインを使用します。


2
@DavidPashleyあなたのソリューションは私にとって魅力的でした。おかげで
Jayeshゴパラン

1
If you are using the new dual HTTP and HTTPS server definitionその後、分離する必要があります。
VBart

2
エレガントで完璧に動作します!
ジャックトレード14

2
これが私のLaravel / Homestead Nginx構成で機能する唯一のソリューションでした。
ジャレッド・アイトニエ

1
また、return 301 https://$server_name$request_uri;これが好ましい方法であるため、書き換え行もあるべきです。
ジャレッド・アイトニエ

27

Host:リクエストヘッダーを保持し、nginx pitfallsの「GOOD」の例に従う、さらに別のバリアント:

server {
    listen   10.0.0.134:80 default_server;

    server_name  site1;
    server_name  site2;
    server_name  10.0.0.134;

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

結果は次のとおりです。の$server_name代わりにを使用すると、$host常ににリダイレクトされることに注意してくださいhttps://site1

# curl -Is http://site1/ | grep Location
Location: https://site1/

# curl -Is http://site2/ | grep Location
Location: https://site2/


# curl -Is http://site1/foo/bar | grep Location
Location: https://site1/foo/bar

# curl -Is http://site1/foo/bar?baz=qux | grep Location
Location: https://site1/foo/bar?baz=qux

Note that using $server_name instead of $host would always redirect to https://site1それは何の$request_uriためですか?
ユルゲンポール14

2
$request_uriホスト名またはドメイン名が含まれていません。つまり、常に「/」文字で始まります。
ピーター14

2
最良の答え。
アシュシュ

3
この答えがなぜ投票数でそれほど低いのか、私にはわかりません。使用する価値がある唯一のものです。
zopieux

2
これほど多くの人が$ SERVER_NAMEを使用し、これはそれを行うための正しい方法であると考えていカント
グレッグ・エニス

3

Cookieには必ず「セキュア」を設定してください。設定しないと、HTTPリクエストで送信され、Firesheepなどのツールによって取得される可能性があります。


1
server {
    listen x.x.x.x:80;

    server_name domain.tld;
    server_name www.domian.tld;
    server_name ipv4.domain.tld;

    rewrite     ^   https://$server_name$request_uri? permanent;
}

これはうまくいくと思う。xxxxはサーバーのIPを指します。Plesk 12を使用している場合は、目的のドメインのディレクトリ「/var/www/vhosts/system/domain.tld/conf」にある「nginx.conf」ファイルを変更することにより、それを行うことができます。設定を保存した後、nginxサービスを再起動することを忘れないでください。


rewrite ^ https://$host$request_uri? permanent; あなたがバーチャルホスト上で複数のサーバー名を持っている可能性があるため、より良い解決策になる

0

これが最も簡単な解決策だと思います。非HTTPSおよび非WWWトラフィックの両方をHTTPSおよびwwwのみに強制します。

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;
    }
}

編集-2018年4月:IFのないソリューションは、私の投稿で見つけることができます:https//stackoverflow.com/a/36777526/6076984


1
IF条件は、nginxの世界で悪と非効率と見なされていませんか?
PKHunter

はい、一般にそうです。しかし、この単純なチェックではそうではないと思います。しかし、より多くのコード記述を伴う適切な構成ファイルがありますが、IFを完全に回避します。
スタンスター

Googleでは、303ではなく301を使用することをお勧めします。出典:support.google.com/webmasters/answer/6073543
hl

@DylanHunt-テストのためだけに303を去り、最初のハンドラーが301に設定され、2番目だけが変更するのを忘れていたことに注意してください:)また、IFのないソリューション:stackoverflow.com/a/36777526/6076984
stamster
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.