: O. Yuanying

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