Rails application behind a proxy, with SSL, passes paths as "http: //"

To begin with, this sounds more like a bug than anything else.

My rails app is served by Unicorn. Then, using Nginx as a reverse proxy, I serve the application to the outside world using SSL.

So far so good, no problem. I'm using relative paths (Path Helpers), so there shouldn't be any problem to create it (for https://www.example.com ):

new_entry_path => https://www.example.com/entries/new

      

This works great in most cases.

The problem comes up when in the controller I try to redirect the "show" action (using resources), say after a successful update (assume Entry with id 100):

redirect_to @entry, flash: {success: "Entry has been updated"}

      

or

redirect_to entry_path(@entry), flash: {success: "Entry has been updated"}

      

they both redirect to:

http://www.example.com/entries/100 # missing 's' in https...

      

instead

/entries/100 # implying https://www.example.com/entries/100

      

As far as I noticed, this only happens with the action show

and only in the controller redirect.

I'll get around this by doing something awful and disgusting:

redirect_to entry_url(@entry).sub(/^http\:/,"https:"), flash: {success: "Entry has been updated"}

      

Has anyone come across something similar? Any ideas would be greatly appreciated ...

+3


source to share


3 answers


So far, I've managed to work around it by adding a rewrite rule to Nginx, under a simple http:

rewrite ^/(.*)$  https://www.example.com/$1? permanent;

      

Which redirects all simple requests http

to the server https

.

Update:

Apparently since I want the application not to care about how the webserver is serving it, it runs on the same webserver to "clean up the mess" of redirects that the application itself cannot handle if it is not specifically configured (not desirable). So, I'll stick with this answer (workaround rather ...)



UPDATE
After papirtiger's answer, I saw that I was missing flashes, which should be added to the override redirect_to

as a parameter.

But I found a way to make my life much easier by simply overriding another function that is called from redirect_to

.

def _compute_redirect_to_location(options) #:nodoc:
    case options
    # The scheme name consist of a letter followed by any combination of
    # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
    # characters; and is terminated by a colon (":").
    # See http://tools.ietf.org/html/rfc3986#section-3.1
    # The protocol relative scheme starts with a double slash "//".
    when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
        options
    ## WHEN STRING: THIS IS REMOVED TO AVOID ADDING PROTOCOL AND HOST ##
    # when String
    #   request.protocol + request.host_with_port + options
    when :back
        request.headers["Referer"] or raise RedirectBackError
    when Proc
        _compute_redirect_to_location options.call
    else
        url_for(options)
    end.delete("\0\r\n")
end

      

This way, without changing anything in my code, I have a relative redirect working.

+1


source


I had a similar problem. By default, Rails will use the current protocol for *_url

helpers.

We use nginx as web server and unicorn as application server. Nginx accepts the request, unpacks the SSL portion, and then passes it to the unicorn. Hence, the unicorn always gets a request http

. If we want Rails to know about the original protocol, we may need to add a header X-Forwarded-Proto

.



Configuration example:

upstream app {
    server unix:/var/run/myserver/unicorn.sock fail_timeout=0;
}
server {
    listen       443 ssl;
    server_name  myserver;

    ssl_certificate "/etc/pki/nginx/myserver.crt";
    ssl_certificate_key "/etc/pki/nginx/private/myserver.key";

    root /myserver/public;

    try_files $uri/index.html $uri @app;
    sendfile on;

    location @app {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https; # <--- will be used
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app;
   }
}

      

+4


source


I think force_ssl

this is what you are looking for.

class AccountsController < ApplicationController
  force_ssl if: :ssl_configured?

  def ssl_configured?
    !Rails.env.development?
  end
end

      


Edit , if you really want to redirect to relative paths, you can always create your own helper:

module ActionController
  module RelativeRedirectingHelper
    extend ActiveSupport::Concern

    include AbstractController::Logger
    include ActionController::RackDelegation
    include ActionController::UrlFor

    def redirect_to_relative(path, response_status = 302) #:doc:
      raise ActionControllerError.new("Cannot redirect to nil!") unless options
      raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
      raise AbstractController::DoubleRenderError if response_body

      self.status        = response_status
      self.location      = path
      self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
    end
  end
end

      

This is a quick'n'dirty copy paste operation. It takes a little more effort if you want to have the same signature asredirect_to

+1


source







All Articles