web-dev-qa-db-ja.com

NGINXでHTTPSをHTTPにリダイレクトするにはどうすればよいですか?

ドメインのvhostファイルにルールを追加して、HTTPSリクエストをHTTPにリダイレクトする方法はありますか?

43
deb

なぜそのようなものが役立つのですか?一見、それができるかどうかわかりませんでした。しかし、それは興味深い質問を提示しました。

設定ファイルにリダイレクト文を入れて、サーバーを再起動してみてください。次の2つの可能性があります。

  1. サーバーはリダイレクトを発行します-あなたが望むように見えます。
  2. サーバーは最初にhttps交換を行い、次にリダイレクトを発行します。その場合、ポイントは何ですか?

もっと具体的なものを思い付くと、さらに追加されます。

更新:(数時間後)これを試すことができます。これをnginx.confファイルに入れる必要があります-

server {
       listen 443;
       server_name _ *;
       rewrite ^(.*) http://$Host$1 permanent;
 }

クライアントに永続的なリダイレクトを送信します。 httpsにポート443(デフォルト)を使用していると仮定しています。

server {
    listen      80;
    server_name _ *;
    ...
}

これを追加して、ポート80での通常のhttp要求が妨げられないようにします。

PDATE:2016年12月18日-nginxバージョンではserver_name _の代わりにserver_name _ *を使用する必要があります> 0.6.25(@Luca Steebに感謝)

36

rewriteifは、Nginxでは使用しないでください。有名な行は、「NginxはApacheではありません」です。言い換えると、Nginxには書き換えよりもURLを処理するより良い方法があります。 returnはまだ技術的には書き換えモジュールの一部ですが、rewriteのオーバーヘッドを運ばず、ifほど警告を受けません。

Nginxには なぜifが "邪悪" のページ全体があります。また、 なぜrewriteifが悪いのか とその回避方法を説明する建設的なページも提供します。 rewriteおよびifに関してページが言っていることは次のとおりです。

これは間違った、面倒な、効果のない方法です。

returnを使用して、この問題を適切に解決できます。

server {
    listen 443 ssl;

    # You will need a wildcard certificate if you want to specify multiple
    # hostnames here.
    server_name domain.example www.domain.example;

    # If you have a certificate that is shared among several servers, you
    # can move these outside the `server` block.
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/cert.key;

    # 301          indicates a permanent redirect.  If your redirect is
    #              temporary, you can change it to 302 or omit the number
    #              altogether.
    # $http_Host   is the hostname and, if applicable, port--unlike $Host,
    #              which will break on non-standard ports
    # $request_uri is the raw URI requested by the client, including any
    #              querystring
    return 301 http://$http_Host$request_uri;
}

Hostヘッダーを送信しない多くのボットが予想される場合、ポート80および443に固執する限り、$Hostの代わりに$http_Hostを使用できます。 '$http_Host代替を動的に設定する必要があります。このコードは、serverを使用しているにもかかわらず、locationブロックではなくifのルートにある限り、効率的で安全です。ただし、これを適用するにはデフォルトのサーバーを使用する必要があります。これはhttpsで回避する必要があります。

set $request_Host $server_name:$server_port;
if ($http_Host) {
    set $request_Host $http_Host;
}

特定のパスにSSL/TLSを適用したいが、そうでない場合は禁止する場合:

server {
    listen 443 ssl;
    server_name domain.example;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/cert.key;

    location / {
        return 301 http://$Host$request_uri;
    }

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

server {
    listen 80;
    server_name domain.example;

    location / {
        try_files $uri =404;
    }

    location /secure/ {
        return 301 https://$http_Host$request_uri;
    }
}

サーバーがクライアントと直接通信していない場合-たとえば、CloudFlareを使用している場合-物事はもう少し複雑になります。クライアントと直接通信しているサーバーが適切なX-Forwarded-Protoヘッダーをリクエストに追加することを確認する必要があります。

これを使用するのは面倒な提案です。完全な説明については、 IfIsEvil を参照してください。これを有効にするには、さまざまな複雑な理由により、ifブロックをlocationブロックの中に入れることはできません。これにより、URIテストにrewriteの使用が強制されます。 要するに、実稼働サーバーでこれを使用する必要がある場合は...しないでください。このように考えてください。Apacheが大きくなった場合、このソリューションは大きくなりました。

/ secure、/ secure /、および/ secure /にあるものはすべてhttpsを強制し、他のすべてのURIはhttpを強制します。 (?! ) PCREコンストラクトは、 負の先読みアサーション です。 (?: )非キャプチャグループ です。

server {
    # If you're using https between servers, you'll need to modify the listen
    # block and ensure that proper ssl_* statements are either present or
    # inherited.
    listen 80;
    server_name domain.example;

    if ($http_x_forwarded_proto = https) {
        rewrite ^(?!/secure)/ http://$http_Host$request_uri? permanent;
    }
    if ($http_x_forwarded_proto != https) {
        rewrite ^/secure(?:/|$) https://$http_Host$request_uri? permanent;
    }
}
35
Zenexer
location / {
    if ($scheme = https) {
        rewrite ^(.*)? http://$http_Host$1 permanent;
    }
}
6
ansiart

この質問は、serverfault.comサイトにより適しています。

Httpへのリダイレクトを行うより良い方法:

server {
   listen 443;
   return 301 http://$Host$request_uri;
}

これにより、これまでの他のソリューションの機能である書き換えの「if」句と正規表現の両方が回避されます。どちらもパフォーマンスに影響しますが、実際には、問題になる前に大量のトラフィックが必要になります。

設定によっては、listen句でipを指定し、おそらく上記でservername句を指定することもできます。そのまま、すべてのドメイン名のすべてのポート443要求に適用されます。通常、httpsを使用してドメインごとにIPが必要なため、ほとんどの場合、上記をIPに関連付けることはドメイン名に関連付けることよりも重要ですが、たとえば、すべてのドメインが1つのドメインのサブドメインである場合などです。

編集:TLSは現在、ユニバーサルに近いものであり、単一のIPを共有する複数のドメイン上のHTTPSサイトを可能にするサーバー名識別(SNI)を備えています。良い説明があります こちら

4
mc0e

これは私を助けました:

server {
    listen 443;
    server_name server.org www.server.org;
    rewrite ^ http://$server_name$request_uri? permanent;
}
0
devcline

唯一の単純なルールは、上記の投稿ですでに説明されています。

server {
    listen ip:443;
    server_name www.example.com;
    rewrite ^(.*) http://$Host$1 permanent;
}
0
Maarten