Configuring Rails 3 to use HTTPS and SSL

This article targets Rails 3

The article was written as of Rails 3.1. The information contained in this page might not apply to different versions.

There are several ways to force your Rails application to use SSL and the HTTPS protocol.

Rails >= 3.1

If you are using Rails 3.1 (currently available in beta1) or greater, this commit makes it incredibly easy to switch from HTTP/HTTPS and vice-versa.

Simply use config.force_ssl = true in your environment configuration.

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.force_ssl = true
  end
end

You can also selectively enable https depending on the current Rails environment. For example, you might want to keep HTTPS turned off on development, and enable it on staging/production.

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.force_ssl = false
  end
end

# config/environments/production.rb
MyApp::Application.configure do
  config.force_ssl = true
end

Behind the scenes, Rails adds the awesome Rack::SSL Rack middleware to your application middleware stack. Rack::SSL automatically filters the request, redirects not-HTTPS requests to the corresponding HTTPS path and applies some additional improvements to make sure your HTTPS request is secure.

Rails < 3.1

If you're not using Rails 3.1 don't worry. Enabling HTTPS is as easy as adding the following line to your environment configuration.

config.middleware.insert_before ActionDispatch::Static, "Rack::SSL"

Note that I'm passing Rack::SSL as string to delegate the loading of the class at the end of the Rails application initialization. Also note the middleware must be inserted in a specific position in the stack, at least before ActionDispatch::Static and ActionDispatch::Cookies.

Don't forget to define Rack::SSL dependency in your Gemfile.

# Gemfile
gem 'rack-ssl', :require => 'rack/ssl'

Enabling HTTPS and HTTP in parallel

Transitioning an HTTP-based application to HTTPS might not be as easy as you think, especially if your application embeds assets from third-party hosts.

There are cases where you want to make your application available using both HTTP and HTTPS. For instance, we adopted this strategy in RoboDomain for a while when we moved the application to HTTPS.

Rack::SSL has a very interesting and undocumented feature. You can pass an :exclude option to determine when to enable/disable the use of HTTPS.

The following code enables Rack::SSL and all its filters only in case the request comes from a HTTPS connection.

config.middleware.insert_before ActionDispatch::Static, Rack::SSL, :exclude => proc { |env| env['HTTPS'] != 'on' }

Both the following URLs will continue to work, but the first one will trigger the Rack::SSL filters.

https://example.com
http://example.com

This solution also works very well for cloud providers like Heroku and completely replaces home-made solutions like the one I posted in this StackOverflow answer months ago, also featured here and here.

If you want to use SSL on Heroku and redirect the HTTP traffic to HTTPS, my suggestion is to go with Rack::SSL.