为什么我在Rails应用程序中使用force_ssl获取无限redirect循环?

我想让我的API控制器使用SSL,所以我添加了另一个listen指令到我的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; } 

它通过nginx conftest没有任何问题。 我还为我的ApiController添加了一个force_ssl指令

 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 

这工作得很好,当我不使用SSL,但现在当我尝试curl --LI http://foo/api/auth.json ,我得到正确的redirect到https ,但后来我一直在redirect到http://foo/api/auth以无限redirect循环结束。

我的路线只是有

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

我在Ruby 1.9.2上使用Rails 3.2.1和nginx 0.7.65

您不会转发有关此请求是否是HTTPS终止的请求的任何信息。 通常,在服务器中,“ssl on” 指令将设置这些标题,但是您正在使用组合的块。

Rack(和force_ssl)通过以下方式确定SSL:

  • 如果请求进入端口443(这可能不会从nginx传回给Unicorn)
  • 如果ENV ['HTTPS'] ==“打开”
  • 如果X-Forwarded-Proto标头==“HTTPS”

查看完整故事的force_ssl来源 。

由于您使用的是组合块,因此您需要使用第三种forms。 尝试:

 proxy_set_header X-Forwarded-Proto $scheme; 

在你的服务器或位置块每个nginx文档 。

当您进入端口80的请求时,这会将标头设置为“http”,当您进入443请求时将其设置为“https”。

尝试在你的nginx location @unicorn块设置这个指令:

proxy_set_header X-Forwarded-Proto https;

我有这个相同的问题,并调查Rack中间件处理程序(不force_ssl但类似),我可以看到,它正在设置,以确定该请求已被处理为由nginx SSL。