Sunday, December 24, 2006

Redirecting to HTTPS in Rails

There are a couple simple steps to take to secure your login pages with rails. First, you'll need a certificate - this I got through my ISP, so I don't have much insight into this. It was uneventful and worked right away.

Once you have the certificate and HTTPS works for your site, you'll need a before filter on any pages that should be https and another one to send all other pages to http. For example:

before_filter :require_https, :only => [:login, :signup, :change_password] before_filter :require_http, :except => [:login, :signup, :change_password]

def require_https
redirect_to :protocol => "https://" unless (@request.ssl? or local_request?)

def require_http
redirect_to :protocol => "http://" if (@request.ssl?)

This will put any pages that should be HTTPS in the right protocol.

And, if you have a login form on an HTTP page (this may be on the home page of a sight), you'll need the form action to point to the https version of the form. To do this, set the "start_form_tag" helper like so:

<%= start_form_tag({:protocol => 'https://', :controller => 'user', :action=> "login", :only_path=> false}) %>

This is necessary to set the protocol to https. Note, the "only_path" value is set to "false". This is because "only_path" defaults to true and will ignore the protocol unless it is set to false.


  1. Anonymous4:24 PM

    Hey Mark,

    thanks for the article. I used it to build a filter method I included in my ApplicationController Class

    before_filter :redirect_to_required_protocol

    def redirect_to_required_protocol
    case self.class.to_s
    when "LoginController"

    So I can switch to https for example when someone is requesting methods of the LoginController. And every other Controller will redirect back to http.
    I'm sure one can easily extend to controller methods by inspecting the params[:action] parameter.



  2. Hey Mark,

    It appears that this solution doesn't work with POSTs, just with GETs. This is because redirect_to sends a 3xx header to the browser, which will then do a GET on the specified URL. So, if we have a "POST login" URL, for example, the require_http filter that you suggested will send a "GET login", instead.

    Have you worked around this, already?