65 votes

Pourquoi suis-je infini de redirection en boucle avec force_ssl dans mon application Rails?

Je veux avoir mon API contrôleur de l'utilisation de SSL, j'ai donc ajouté une autre directive listen mon 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;
}

qui passe la nginx conftest sans aucun problème. J'ai également ajouté un force_ssl directive à mon 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

qui a très bien fonctionné quand je n'étais pas à l'aide de SSL, mais maintenant, quand j'essaie d' curl --LI http://foo/api/auth.json, j'ai bien redirigé vers https, mais puis-je continuer à obtenir redirigé vers http://foo/api/auth se terminant en une infinité de boucle de redirection.

Mes itinéraires tout simplement

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

Je suis à l'aide de Rails 3.2.1 sur Ruby 1.9.2 avec nginx 0.7.65

126voto

Chris Heald Points 28814

Vous n'êtes pas la redirection de toutes les informations au sujet de savoir si cette demande est un HTTPS terminée par le demande ou non. Normalement, dans un serveur, le protocole ssl "sur;" directive fixera ces en-têtes, mais vous êtes en utilisant une combinaison de bloc.

Rack (et force_ssl) détermine SSL par:

  • Si la demande est faite sur le port 443 (ce qui est susceptible de ne pas être passé à la Licorne de nginx)
  • Si ENV['HTTPS'] == "on"
  • Si le X-Forwarded-Proto-tête == "HTTPS"

Voir la force_ssl source pour l'histoire complète.

Puisque vous êtes en utilisant une combinaison de bloc, vous souhaitez utiliser la troisième forme. Essayez:

proxy_set_header X-Forwarded-Proto $scheme;

dans votre serveur ou emplacement en bloc par la nginx documentation.

Ceci permettra de définir l'en-tête de "http" quand vous venez sur le port 80 de demande, et de le configurer en mode "https" quand vous venez sur un 443 demande.

21voto

Cody Caughlan Points 18780

Essayez de définir la présente directive dans votre nginx location @unicorn bloc:

proxy_set_header X-Forwarded-Proto https;

J'ai eu ce même problème et l'enquête sur les middleware Rack gestionnaire (pas force_ssl , mais de même), je pouvais voir qu'elle s'attendait à ce que l'en-tête pour déterminer si la demande a déjà été traitée comme étant SSL par nginx.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X