Why am I getting an infinite redirect loop with force_ssl in my Rails application?

I want my API controller to use SSL, so I added another directive to listen on my nginx.conf

upstream unicorn { server unix:/tmp/unicorn.foo.sock fail_timeout=0; } server { listen 80 default deferred; listen 443 ssl default; ssl_certificate /etc/ssl/certs/foo.crt; ssl_certificate_key /etc/ssl/private/foo.key; server_name foo; root /var/apps/foo/current/public; try_files $uri/system/maintenance.html $uri/index.html $uri @unicorn; location @unicorn { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://unicorn; } error_page 502 503 /maintenance.html; error_page 500 504 /500.html; keepalive_timeout 5; } 

which without any problems passes nginx conftest. I also added a force_ssl directive to my ApiController

 class ApiController < ApplicationController force_ssl if Rails.env.production? def auth user = User.authenticate(params[:username], params[:password]) respond_to do |format| format.json do if user user.generate_api_key! unless user.api_key.present? render json: { key: user.api_key } else render json: { error: 401 }, status: 401 end end end end def check user = User.find_by_api_key(params[:api_key]) respond_to do |format| format.json do if user render json: { status: 'ok' } else render json: { status: 'failure' }, status: 401 end end end end end 

which worked fine when I did not use SSL, but now when I try to execute curl --LI http://foo/api/auth.json , I am redirecting to https correctly, but then I continue to redirect to http://foo/api/auth , ending with an infinite redirect loop.

My routes just have

 get "api/auth" get "api/check" 

I am using Rails 3.2.1 on Ruby 1.9.2 with nginx 0.7.65

+51
redirect ruby-on-rails ssl ruby-on-rails-3 nginx
Feb 25 '12 at 21:18
source share
2 answers

You do not forward any information about whether this request was completed by an HTTPS request or not. Usually on the server "ssl on;" the directive will set these headers, but you are using a combo box.

Rack (and force_ssl) defines SSL through:

  • If the request arrives on port 443 (this will most likely not be returned by Unicorn from nginx)
  • If ENV ['HTTPS'] == "on"
  • If the header is X-Forwarded-Proto == "HTTPS"

See the force_ssl source for more details .

Since you are using a combo box, you want to use the third form. Try:

 proxy_set_header X-Forwarded-Proto $scheme; 

in your server or location block for nginx documentation .

This will cause the header to be “http” when you enter the request on port 80, and set it to “https” when you enter the request 443.

+105
Feb 25 2018-12-21T00:
source share

Try setting this directive in your nginx location @unicorn :

proxy_set_header X-Forwarded-Proto https;

I had the same problem and an investigation of the Rack middleware handler (not force_ssl , but similar). I could see that he expected this header to be set to determine if the request was already processed as SSL using nginx.

+16
Feb 25 2018-12-21T00:
source share



All Articles