简体   繁体   English

代理后面的Rails应用程序,使用SSL,将路径呈现为“http://”

[英]Rails application behind proxy, with SSL, renders paths as “http://”

To start with, this sounds more like a bug then anything else. 首先,这听起来更像是一个错误,然后是其他任何东西。

My rails application is served by Unicorn. 我的rails应用程序由Unicorn提供服务。 Then, using Nginx as a reverse proxy, I serve the application to the outside world using SSL. 然后,使用Nginx作为反向代理,我使用SSL将应用程序提供给外部世界。

So far so good, no problem. 到目前为止这么好,没问题。 I'm using relative paths (Restful path helpers), so there should be no problem to produce this (for https://www.example.com ): 我正在使用相对路径(Restful path helpers),所以生成它应该没有问题(对于https://www.example.com ):

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

This works fine in most cases. 在大多数情况下,这样做很好。

The problem however appears when in a controller I try to redirect to a "show" action (using resources), let's say after a successful update (suppose Entry with id 100): 然而,在控制器中我尝试重定向到“显示”操作(使用资源)时出现问题,假设在成功更新后(假设条目为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 produce a redirect to: 他们都产生了重定向:

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

instead of 代替

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

As far as I've noticed, this only happens with show action and only in controller redirects. 据我所知,这只发生在show动作中,只在控制器重定向中发生。

I'm bypassing this by doing something horrible and disgusting: 做一些可怕而恶心的事情,我绕过了这个:

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

Has anyone ever confronted something similar? 有没有人遇到类似的东西? Any ideas will be gratefully accepted... 任何想法都将被感激地接受......

I had a similar problem. 我遇到了类似的问题。 By default Rails will use the current protocol for the *_url helpers. 默认情况下,Rails将使用当前协议用于*_url助手。

We use nginx as web server and unicorn as application server. 我们使用nginx作为Web服务器,使用unicorn作为应用程序服务器。 Nginx takes the request, unwrapps the SSL part and then passes it on to unicorn. Nginx接受请求,解开SSL部分,然后将其传递给独角兽。 Hence, unicorn always receives a http request. 因此,独角兽总是收到一个http请求。 If we now want Rails to know about the original protocol, we can need to add the X-Forwarded-Proto header. 如果我们现在希望Rails知道原始协议,我们可能需要添加X-Forwarded-Proto头。

Example config: 示例配置:

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

So far, I've managed to get a workaround by adding a rewrite rule to Nginx, under plain http: 到目前为止,我已经设法通过在简单的http下为Nginx添加重写规则来获得解决方法:

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

Which redirects all plain http requests to the https server. 这会将所有普通的http请求重定向到https服务器。

Update: 更新:

Apparently, as I want the app not to care about how the front web server is serving it, it's up to the same web server to "clean the mess" of redirections the app by itself cannot handle unless it's been specifically configured (not desired). 显然,因为我希望应用程序不关心前端Web服务器如何服务它,所以由同一个Web服务器来“清除重定向的混乱”应用程序本身无法处理,除非它是专门配置的(不需要) 。 So, I will stick to this answer (workaround rather...) 所以,我会坚持这个答案(解决方法而不是...)

UPDATE UPDATE
Following papirtiger's answer, I saw that I ended up missing the flashes, which should be added to the overriding redirect_to as a parameter. 在papirtiger的回答之后,我看到我最终错过了闪光,这些闪光应作为参数添加到重写的redirect_to中。

But I've found a way to do my life a lot easier, simply by overriding a different function, that is called from within redirect_to . 但是我已经找到了一种方法,可以更轻松地完成我的生活,只需重写一个不同的函数,即从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

Thus, without having to change anything else in my code, I have a working relative redirect. 因此,无需更改我的代码中的任何其他内容,我有一个工作相对重定向。

I think force_ssl is what you are looking for. 我认为force_ssl就是你要找的东西。

class AccountsController < ApplicationController
  force_ssl if: :ssl_configured?

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

Edit , if you really want to do redirects to relative paths you could 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 job . 这是一个快速复制粘贴工作。 Will take a little more effort if you want have the same signature as redirect_to 如果您想要与redirect_to具有相同的签名,则需要更多努力

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM