Nginx + SSL + Rails
普通に nginx をフロントに置いて、バックエンドに Rails を置いた場合の nginx の設定ファイルは、 半年くらい前に書いた。
これはこれで良いのだが、今度は HTTPS でもアプリケーションにアクセスしたくなった場合、 listen 80 の server セクションをコピーして、listen 443 で SSL を利用する server セクションを追加すれば良いだけの気もするのだが。 単純にコピーして SSL の設定をしただけだとはまることがある。
force_ssl
Rails 3.x からだと思うが、ActionController に force_ssl というクラスメソッドが追加された。
# Force the request to this particular controller or specified actions to be
# under HTTPS protocol.
#
# Note that this method will not be effective on development environment.
#
# ==== Options
# * only - The callback should be run only for this action
# * except - The callback should be run for all actions except this action
def force_ssl(options = {})
host = options.delete(:host)
before_filter(options) do
if !request.ssl? && !Rails.env.development?
redirect_options = {:protocol => 'https://', :status => :moved_permanently}
redirect_options.merge!(:host => host) if host
redirect_to redirect_options
end
end
end
ようするに SSL でアクセスしているかを判断して、SSL 以外でアクセスしていた場合、 SSL を利用した URL にリダイレクトしてくれる before_filter だ。
ただ、フロントに nginx を置いて、バックエンドに Rails を置いた場合は、 普通、SSL の処理をフロントに任せるので以下のようになる。
何も対策を施さなければ、force_ssl 処理内の request.ssl? が false を返して、 リダイレクトの無限ループになってしまう。ってかなった。
X-FORWARDED_PROTO
そんなバックエンドが SSL を利用された後であるかどうかを判断するために、 HTTP には X-FORWARDED_PROTO とかいうヘッダが用意されているらしい。へえー。
と、言う訳でリバースプロキシの設定に以下の記述を行えばおk。
try_files /system/maintenance.html $uri $uri/index.html $uri.html @application; location @application { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-FORWARDED_PROTO https; proxy_pass http://application; }