SSL が必要な簡単なページを稼働させる際に、ELB の無料の SSL 証明書を利用した時にハマったお話
構成
簡単な図です。
- ec2 の前段に ELB
- HTTPS(SSL) は ELB が対応
- ec2 へのアクセスは http
- http -> https へのリダイレクトは nginx 側で対応
- ec2 は一台のみ
問題が起きた設定
http://qiita.com/snoguchi/items/f5ccb67592f87942480d
はじめに、こちらの記事を参考に以下のように設定しました。
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name example.com; # 利用するドメインを設定
# アクセスが http なら https としてリダイレクト
if ($http_x_forwarded_proto != https) {
return 301 https://$host$request_uri;
}
location / {
root /var/www/index/;
index index.html;
}
}
これで一時はうまくいったのですが、しばらくするとページにアクセスできなくなり、503 Service Unavailable: Back-end server is at capacity
というステータスが返されて表示できなくなりました。
どうやら ELB のヘルスチェックがこけて、ec2 が外されたようでした。
原因
ヘルスチェック自体は http で行っていたので、$http_x_forwarded_proto
は http。
https へのリダイレクトの条件に合致して、ステータスコード 301 を返したのでヘルスチェックで fail 判定となって外された。
というのが原因だと推測されました。
対処
対処はいくつか考えられます。
- ヘルスチェック用のルーティングを追加 (/healthcheck 等)
- 別のポートで Server コンテキストを追加
- default_server と server_name で別の server コンテキストを作成して分ける
他にもやり方は色々とあるかと思います。
ここでは一番最後の default_server と server_name で別の server コンテキスト作成して分ける方法で対応しました。
既存のドメイン指定の server コンテキストの default_server
を削除して、ヘルスチェック用の 1px gif を返すだけのコンテキストを追加します。
# health check 用 (とにかく 200 を返せれば OK)
server {
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
location / {
# empty_gif というモジュールで 1x1 の透過 gif を返します
empty_gif;
access_log off;
break;
}
}
server {
# default_server を削除
listen 80;
listen [::]:80;
server_name example.com; # 利用するドメインを設定
# アクセスが http なら https としてリダイレクト
if ($http_x_forwarded_proto != https) {
return 301 https://$host$request_uri;
}
location / {
root /var/www/index/;
index index.html;
}
}
これで問題なく稼働するようになりました。
ただ、上記の設定は IP 直でアクセスすると 1px の gif が表示されます。
対応した案件が、クローズドなサイトで IP でアクセスする必要が皆無の為このような設定になりましたが、IP でもドメインでも同様にアクセスさせたい場合は、ヘルスチェックを考慮した設定を考える必要があると思います。