株式会社ライブキャストロゴ 株式会社ライブキャスト

個人情報を扱っているサイトなどを運営していると、httpリクエストを強制的にhttpsにリダイレクトしたいケースが出てきたりします。

Webサーバがロードバランサー配下にあり、ロードバランサーでSSLを扱っている場合には、ロードバランサーとWebサーバ間の通信は実はhttpで、Webサーバの443ポート空けてるけど意味ないじゃん。みたいなことがあったりします(ないか…)。

実際に、SSLをELB(Amazon Web ServiceのElastic Load Balancer)に導入しているサイトで、強制的にhttpsにリダイレクトさせたいという要件が出てきました。今回はwebサーバ(nginx)の設定でリダイレクトさせるようにしましたのでその一例を示したいと思います。

いろいろなやり方があると思いますが、今回はnginx.confの設定で対応しました。

server {
listen 80;
server_name hoge.example.com;
rewrite ^(.*) https://hoge.example.com$1 permanent;
}

このようにrewriteで特定のリクエストをhttpsにリダイレクトしてやれば良いですが、プロトコルを判定してhttpでリクエストされた時のみリダイレクトするようにしました。

ただ、ELBのListener設定を以下のようにしていると、



ELB経由のリクエストはかならずhttp(ポート80)になるので、$http_x_forwarded_proto変数を使用してELBに対してのリクエストを判定するようにします。

if ($http_x_forwarded_proto != ‘https’) {
xxx;
}

ですが、これだとELBのHealth Checkのリクエストも含まれてしまい、StatusがOut Of Serviceになってしまいます。これを防ぐためにユーザエージェントを判定して、Health Checkのリクエストを除外するようにしました。

if ($http_user_agent !~* ELB-HealthChecker) {
yyy;
}

ここで問題になるのが実IPのインスタンスにアクセスした場合にもリダイレクトされてしまうということです。ELBでSSLを処理している場合には、その配下のインスタンスにはSSLサーバ証明書が設定されてはいませんのでページを表示することができません。
なので、ホスト名を判定してELB経由のアクセスかどうかをチェックします。

if ($http_host ~ “www.example.com”) {
zzz;
}

まとめると、

location / {
root /usr/share/nginx/html;
index index.html index.htm index.php;

set $redirect “”;

if ($http_x_forwarded_proto != ‘https’) {
set $redirect “1”;
}

if ($http_user_agent !~* ELB-HealthChecker) {
set $redirect “${redirect}1”;
}

if ($http_host ~ “www.example.com”) {
set $redirect “${redirect}1”;
}

if ($redirect = “111”) {
rewrite ^ https://$host$request_uri? permanent;
}
}

こんな感じでしょうか。

すべてのif文で条件を満たした場合のみリダイレクトします。
ELB経由のリクエストで、ELBのhealth check以外で、特定のホストにのみ要求されたhttpリクエストのみをhttpsにリダイレクトする設定です。
※ nginx.conf内のif文と変数への代入の制約からこのような書式にしています。

この他にもうまいやり方があると思いますので、色々試してみてください。