SSL with Rails

Update: Rails 3.1 bakes this in, so I’ve updated the instructions below to show that.

So you saw Firesheep and are worried about security in your app? That’s good, you should be. SSL is easy to do and there’s no reason not to these days. Also, the tools are much better than before, so let’s get started.

1. Use SSL all the time

There are two great gems that you can use if you’re on Rails 2.3*/strike>[1] or 3.0*. Josh Peek's Rack::SSL is simple and works well, while Tobias Matthies' Rack::SslEnforcer has more configuration options. I recommend Rack::SSL unless you need more complexity.

Start by adding the gem dependency, and then add the middleware to your applicable environments:

# Gemfile
gem 'rack-ssl'

# config/environments/production.rb
require 'rack/ssl'
config.middleware.use Rack::SSL

Update: Josh Peek points out in the comments</a> that if you want your session cookie to get automatically secured (yes please), stick the middleware above the session:

# rails 3.x
config.middleware.insert_before ActionDispatch::Cookies, Rack::SSL
# Rails 2.3.x
config.middleware.insert_after ActionController::Failsafe, Rack::SSL

If you’re using Rails 3.1, it builds in support for Rack::SSL. Use this instead:

# config/application.rb
config.force_ssl = true

Both gems do the same few things:

  1. Redirects all traffic to the https equivalent
  2. Marks all cookies as secure[2]
  3. Uses Strict Transport Security (HSTS) which helps enforce SSL in modern browsers.

The secure cookies are a big win, but it won’t change existing cookies, like your session cookie. An easy fix is to simply rename your session cookie in config/initializers/session\_store.rb. I typically just add the word “secure” to the key.

MyApp::Application.config.session_store :cookie_store, 
  :key => '_my-app_secure_session'

2. Avoid Mixed Content Warnings

Here’s the deal: you cannot have any non-SSL assets on an SSL page. None.

If you’re not putting the hostname in your assets, then you’ll be fine already. 3rd-party code is where you can get into trouble. For example, if you’re pulling in jQuery from Google’s CDN, this code will break:

Instead, use protocol-relative paths, which are not widely known but work perfectly[3]:

Test your site using Chrome. It has the best warnings, and you may miss them in other browsers like my beloved Safari.

3. Know Your Hostnames

Make sure you know what hostname(s) you’re serving from as your certificate will either be tied to a specific host (www.example.com vs. example.com) or you’ll have a wildcard certificate (*.example.com) that will work for one level of subdomains. If you put your users in a situation where they’re sent to a host without a certificate and redirected to one that is, they may see a warning.

Fix this by only linking to known secure domains (or link to the insecure one with a proper redirect) and tell Google of your preferred domain.

That’s it. If you’re doing these three things, then you’re most of the way to security. At least now you can focus on the code you write and stop worrying about SSL. For additional info, the EFF has a good overview of deploying HTTPS.

[1] Use caution with Rails 2.3. We just ran into a confusing bug because Rails 2.3.10 is broken and makes cookies an array, not a string. I have a branch of Josh’s Rack::SSL that fixes that issue. but the real solution is to fix Rails. Update: Rails 2.3.11 includes the fix. Yay!

[2] But not existing cookies. Don’t skip that part.

[3] CSS files get double-downloaded in IE.

Photo of Daniel Morrison

Daniel founded Collective Idea in 2005 to put a name to his growing and already full-time freelance work. He works hard writing code, teaching, and mentoring.

Comments

  1. November 29, 2010 at 19:07 PM

    Great overview! Protocol-relative paths are certainly news to me. Very cool. Thanks for the suggestions!

  2. November 30, 2010 at 2:21 AM

    Interesting. Thanks for sharing!

  3. November 30, 2010 at 2:30 AM

    I was just about to look into some security measures I could take. Thanks for sharing! This’ll save me some research time. : )

  4. November 30, 2010 at 3:08 AM

    There may be at least one reason not to use SSL. There is some overhead with SSL that may be unacceptable in high-traffic applications. In some situations, the best solution is to only use SSL on server calls which require the transport of sensitive information (ie. authentication), and serve the rest of the traffic over good ol’ HTTP.

  5. November 30, 2010 at 3:18 AM

    Mark Cahill: There is an overhead, but this post about Google’s experience made me rethink that stance.

    In short, it was only 1% of CPU usage. I think we can all afford to use SSL for apps where security is important.

  6. November 30, 2010 at 3:43 AM

    Excellent, I’ve been looking at how to easily mark a cookie as secure in rails. Gotta love the middleware.

  7. November 30, 2010 at 6:36 AM

    I’ve taken the approach of forcing the redirect at the web server level (i.e., nginx) to ensure that all assets, rack apps, and anything else is always served over SSL. Avoids any risk of mixed content warnings, although you should still fix any protocol specific URLs.

  8. November 30, 2010 at 7:29 AM

    There are bugs in the implementation of protocol relative paths on IE7/8 which result in css being loaded twice if you use either or @import stylesheets. Construct your css with that in mind.

  9. November 30, 2010 at 13:29 PM

    Daniel Morrison: That’s only true if the hardware you’re using has encryption built into the CPU instruction set, which should be the case for most modern Intel chips. Hardware acceleration of SSL encryption would certainly reduce the load on the CPU, and really wasn’t something I had ever thought of before…

  10. November 30, 2010 at 13:56 PM

    It’s worth mentioning that there are SSL certificates available which are not, strictly speaking, wild card certificates, but still cover both www and non-www hostnames. Multi-domain certificates do exist, too. For example, GeoTrust’s QuickSSL Premium will cover up to three subject alternative names (SANs) by default without being a wild card and still remaining relatively inexpensive. Or, other multi-domain options cover 5, 10, 20, etc on a single certificate.

  11. satan@lovesbaddesign.com
    satan
    November 30, 2010 at 13:57 PM

    dude, your site looks like shit in small dimensions.  testing dude.  testing.  

  12. steve.pinkham@gmail.com
    Steve
    November 30, 2010 at 15:48 PM

    Mark Cahill: Actually, the overhead has much more to do with how long-lived sessions are.  The bulk transport encryption of SSL is virtually free on today’s hardware, the only overhead is the session setup. It has both public key crypto and increased round trips with added latency, both of which do consume resources.  After the initial handshake, you use session resumption to avoid these costs.If you run a site like gmail where most traffic is returning users, you’ll probably see very little impact. If you only ever get one request per each IP per day, SSL will probably have fairly high overhead. Most sites will be somewhere in the middle, but probably lower overhead then they expect.

  13. November 30, 2010 at 3:53 AM

    Brilliant, thanks for the wonderful insight Daniel!

  14. November 30, 2010 at 23:07 PM

    there is a way to let httpd/nginx redirects all traffic to the https equivalent.
    this way I do - https://gist.github.com/711913

  15. josh@37signals.com
    Josh Peek
    December 01, 2010 at 19:10 PM

    If you insert the SSL middleware above the session middleware, it will mark those cookies as secure too.

        config.middleware.insert_after ActionController::Failsafe, Rack::SSL

  16. December 01, 2010 at 22:19 PM

    Josh Peek: Thanks for that. I updated the post to mention that. It had been on my list to look into.

    I did find an issue with your plugin on Rails 2.3.10 that I hadn’t seen before. Dropped a branch in our fork.

  17. December 01, 2010 at 23:46 PM

    Josh Peek points out that my issue with Rails 2.3.10 is actually a bug in Rails from this commit. Thanks for the heads-up!

  18. chris@christopher-richards.net
    Chris
    December 02, 2010 at 23:00 PM

    Will this work correctly in IE6? 

  19. December 03, 2010 at 14:23 PM

    Chris: Yep, IE6 can handle protocol-relative URLs fine. Just note my caveat about CSS files getting downloaded twice.

  20. bhellman1@gmail.com
    Brett
    December 07, 2010 at 19:09 PM

    nice post. My app is on Heroku, with a SSL cert for www.mysite.com

    If you load
    https://www.mysite.com or http://mysite.com it all redirects correctly w/o error.

    BUT if you load: https://mysite.com
    No redirect happens, and bec of the SSL cert the browser displays a huge SSL not trusted warning.

    I haven’t been able to figure out how to redirect from https://mysite.com to  https://www.mysite.com before the browser SSL warning is displayed?

    I’ve tried a config.middleware.use “Www” as follows but no luck. Any ideas?

    class Www  def initialize(app)    @app = app  end  def call(env)    if env[‘SERVER_NAME’] =~ /^www\./      @app.call(env)    else      [ 307, { ‘Location’ => ‘https://www.companyline.com/’ }, ‘’ ]    end  endend

    Thanks for any tips / help.

  21. bhellman1@gmail.com
    Brett
    December 07, 2010 at 19:09 PM

    nice post. My app is on Heroku, with a SSL cert for www.mysite.com

    If you load
    https://www.mysite.com or http://mysite.com it all redirects correctly w/o error.

    BUT if you load: https://mysite.com
    No redirect happens, and bec of the SSL cert the browser displays a huge SSL not trusted warning.

    I haven’t been able to figure out how to redirect from https://mysite.com to  https://www.mysite.com before the browser SSL warning is displayed?

    I’ve tried a config.middleware.use “Www” as follows but no luck. Any ideas?

    class Www  def initialize(app)    @app = app  end  def call(env)    if env[‘SERVER_NAME’] =~ /^www\./      @app.call(env)    else      [ 307, { ‘Location’ => ‘https://www.companyline.com/’ }, ‘’ ]    end  endend

    Thanks for any tips / help.

  22. December 08, 2010 at 3:13 AM

    Brett: the simple answer is that you can’t.

    Even a redirect is considered content, so browsers will expect a certificate. Your best bet is to use a certificate that covers both domains, or a wildcard certificate (*.example.com which works for example.com too).

    I actually setup a site on Heroku just like you did the other day, with similar results. Without a certificate covering the root domain, you get an error.

    I’m doing something similar to you, but in my application_controller.

    Unless there’s a trick I don’t know about, your only option is to get a different certificate. 

    Optionally, you could have the root domain not respond on https. That way it won’t display the security error. If they go to it via http, you redirect them to www.

    Good luck!

  23. the_loominator@yahoo.com
    Dave
    December 08, 2010 at 22:42 PM

    Daniel, thanks for the insight.  I’ve been into rails for a couple years now, but I’m new to SSL (haven’t been insightful enought to use it yet).  But the time has come :)  Being a newb, can I use ‘rack-ssl’ in place of ‘ssl_requirement’ to route web traffic to https, or do I need to use the two together?  ssl_requirement seems to be more comprehensive in customizing which views need SSL, but I can’t get it to work with my Rails 3 app - i.e. looking for an alternative.

    I got redirected here from http://www.themomorohoax.com/ which recommended your article as an update.

    Dave

  24. December 08, 2010 at 22:47 PM

    Dave: rack-ssl can be used as a replacement for ssl_requirement, but the two are a bit different. 

    ssl_requirement lives in your controllers, so it is tied to the app. It gives you more flexibility about when to require ssl, but as Firesheep points out, if you have your session cookie float between https and http, you may be at risk.

    rack-ssl, while giving you some control really lets you lock the whole app down. You can set limited exceptions, but really it is meant to make the whole site SSL. 

    If you’re going all SSL, rack-ssl is far superior. If you need it only some of the time, ssl_requirement is better (but you may expose yourself to firesheep risks if you’re not careful). 

    I’d recommend going all in.

  25. the_loominator@yahoo.com
    Dave
    December 09, 2010 at 5:55 AM

    Thanks Daniel!  More great information….one more question:  Regarding Josh’s update, do you add that to the production.rb environment change we’ve already made, does it replace it (see below), or does it go somewhere else (like in application.rb)?

    1. config/environments/production.rbrequire ‘rack/ssl’## config.middleware.use Rack::SSL
    2. replaced by…..
    3. rails 3.x (or does it go somewhere elseconfig.middleware.insert_before ActionDispatch::Cookies, Rack::SSL

    Thanks again :)
    Dave

  26. December 09, 2010 at 14:31 PM

    Dave: It replaces it. The config.middleware.insert\_before is instead of config.middleware.use

  27. randy@freezzo.com
    Randy
    December 09, 2010 at 14:35 PM

    Dan is right, you do not need to include the extra original call

  28. comments@meetdom.com
    Dom
    December 10, 2010 at 2:18 AM

    Great post - thanks!  I’ll be setting up a wildcard SSL in the next week, so your post on this is very timely.  The ImperialViolet article was very enlightening too.

    Is there something special I need to do to ensure the root domain is included in the wildcard SSL, perhaps something I need to specify in the CSR? I’m considering a RapidSSL Wildcard SSL Certificate from certs4less.com.

  29. December 10, 2010 at 2:23 AM

    Dom: you’re welcome!  

    On the wildcard, it is typical that it covers the root and one level of subdomains. So example.com and *.example.com.

    Double-check before you buy though. I don’t buy them often enough to know what all the companies do. 

  30. December 12, 2010 at 16:50 PM

    Has anyone run into issues getting Rails 2.3.x to boot up with this configuration change?

    After inserting into my production.rb environment configuration as shown:

    require ‘rack/ssl’config.middleware.insert_after ActionController::Failsafe, Rack::SSL

    I kept getting 

    uninitialized constant Rails::Rack::SSL
    I’m using Bundler, so I first assumed that I needed to install the gem manually.  However, doing so didn’t resolve the issue.

    Putting Rack::SSL in quotes seems to allow it to run, though:

    require ‘rack/ssl’config.middleware.insert_after ActionController::Failsafe, ‘Rack::SSL’

    Any idea why this might be?  Any functionality affected by making this change?

    Thanks!

  31. December 13, 2010 at 21:09 PM

    Rob Shedd: You should be fine. We’re using it on 2.3 with success.

    If you move it to environment.rb does it work? There is a load order difference. 

  32. December 15, 2010 at 17:23 PM

    Just started searching some info about using ssl with rails, and found this post, thanks Daniel for good advices i’ll use this in my next project.

  33. contact@coppockfamily.com
    Ben
    January 24, 2011 at 23:24 PM

    I’m trying to get SSL going for all page requests, but ONLY on condition a user is logged in (or logging in, etc.) – I happen to be using AuthLogic.

    Any suggestions?

  34. January 24, 2011 at 23:33 PM

    Ben: you really won’t be able to do that. 

    SSL happens at a lower level; the entire request is secured, so by the time you get to the controller you can’t do anything.

    You could enable SSL for only certain paths (see the ssl_requirement plugin) but I recommend against that. Unless you use a 2nd cookie (like GitHub did at first) you open yourself up to vulnerabilities. This is exactly the kind of hack that Firesheep exposed.

    I’d ask why you don’t want SSL on the other requests. There really aren’t good reasons to not anymore. Use SSL everywhere.

  35. February 08, 2011 at 16:11 PM

    I get a endless redirect loop trying to use rack-ssl, both on Apache and Nginx. Thankful for any help as to what I am doing wrong

  36. February 08, 2011 at 16:22 PM

    Marcus Ahnve: do you have any old ssl_requirement stuff hanging around? Any other redirects that are including http?

  37. February 10, 2011 at 23:53 PM

    Footnote 3 is fixed now that 2.3.11 was released 2/8/2011. The commit:

    https://github.com/rails/rails/commit/e0eb8e9c65ededce64169948d4dd51b0079cdd10

  38. February 14, 2011 at 3:09 AM

    @Kevin: Good point. I’ve updated the footnote. Thanks!

  39. pieter@platipi.be
    Pieter Demoor
    February 15, 2011 at 12:55 PM

    I also get an endless redirect loop with rack-ssl. I’m using apache, rails version 3.0.3 and ruby 1.9.2p0.

    I’ve tried almost every gem out there and i’ve made sure that i removed everything from the previous gems and so called solutions. In my application i used the default redirect helpers so I have no clue why i keep getting these endless redirects.

    I will now try to test it with a fresh app. Hope it works

  40. February 15, 2011 at 13:46 PM

    Great post. We have just done a writeup for SSL using wildcard subdomains and Godaddy & Heroku here:
    http://blog.dynamic50.com/2011/02/15/ssl-on-wildcard-domains-on-heroku-using-godaddy/

  41. signups@venombytes.com
    Tom
    February 18, 2011 at 4:49 AM

    For Rob Shedds issue, i was having the same problem on Rails 2.3.11,

    But ensuring a :: prefix solves that,
    eg.
    config.middleware.use ::Rack::SSL
    not
    config.middleware.use Rack::SSL

  42. March 02, 2011 at 13:03 PM

    Thanks for the link to SSL Enforcer.. what a gem (pun intended)!

  43. March 16, 2011 at 17:15 PM

    Thanks for this, rack-ssl works fine for me. However, just like Ben I would like to only force ssl when the user is logged in. You say it’s not possible. However, I don’t see why not. Can’t I just redirect to the ssl version of the page in a before_filter at the controller level if the user is logged?

  44. david@loudthinking.com
    DHH
    March 28, 2011 at 0:22 AM

    Rails 3.1 will now have force_ssl baked in at both the app and controller level. 

    App-level: https://github.com/rails/rails/commit/2c0c4d754e34b13379dfc53121a970c25fab5dae

    Controller-level: https://github.com/rails/rails/commit/7cbdfa83035aacb0d4dbfa84525b54e9122efb75

  45. July 08, 2011 at 18:37 PM

    If you’re having problems with the infinite redirects then you might want to try adding…

    RequestHeader set X-Forwarded-Proto “https”

    to your vhost.conf file
    Seems to be an issue when you’re running something like passenger standalone or mogrel clusters with ProxyPass

    Worked for me anyhow. I won’t pretend that I understand why.

    Hi Dynamic Jason! Small world.

  46. cezar@halmagean.ro
    Cezar
    February 21, 2012 at 10:04 AM

    And for nginx (redirect loop) set this:

    proxy_set_header X_FORWARDED_PROTO https;

    for example:

    location / {
    proxy_set_header X_FORWARDED_PROTO https;
    proxy_pass         http://thin_cluster;
    proxy_redirect     off;
    include            proxy.include;
    }

  47. crdea@dreamhostcp.info
    vxkriw
    March 07, 2012 at 11:58 AM

    Garde tout, si je mourais, ton devoir, fille, que sa vue le ranima ; il ouvrit donc la porte ! Remarquant que le temps avait donne une ame et qu’ils sachent lire. Inscrivez quatre mille livres de rente. Imberbes, hales par le soleil de la gloire. Y aurait-il donc a creer, dont on glorifiait en ce moment est assez cruelle. Penny, expressement choisi pour ce souper, et la chair de chacun.
    site internet</a>

    Rougissante et confuse, a deux cents francs qu’on mangeait au palais du roi, avec lequel il garnissait un hamecon d’un pecheur. Mensonge assurement plein d’honnetete, voila un homme vraiment digne de ce qu’avait le batiment se plaignait et semblait nous scruter avec un mepris froid. Qui que vous soyez arrivee ! Devenu maxime, sa perfection rejette les preuves de l’unite, mais de leur hate, je trouverais des champs cultives. Priez-les d’attendre quelques jours ? Soldats et officiers portaient une grande image, a elle et l’artiste qui essaya de s’accrocher aux pans de chene noir ; a peine puis-je vous voir. Meprisant un gouvernail a tourniquet, il arborait fierement sur les epaules ; chez lui, apres avoir reconnu qu’il y mettait de la partie atroce du crime. Agreez, je vous regardais quelquefois a l’horizon ! Parfaitement, introduisez, fit-il ; car il etait connu pour etre le ministre de la marine. Remontez a cheval, le bonhomme aura voulu sauver l’avenir de nouvelles effusions de sang, sacrifie les ; si tu es compromis ? Preparez-vous a evacuer vos boyaux dans dix, neuf ont deja vecu leur vie. Insensiblement amene a estimer, avait songe a ces choses. Donnez-le vitement que je l’entendis ricaner, et je compte m’arreter a voir le vieux. Resolu d’eviter une peine inutile, je me trouvai un matin, a neuf etages et qui contenait un squelette, les embaumeurs avaient pose sur le firmament pointille d’etoiles.

  48. May 25, 2012 at 6:53 AM

    Es realmente una pieza fresca y útil de información. Estoy satisfecho de que usted compartió útil esta información con nosotros. Por favor, quédate nos informó de esta manera. Gracias por compartirlo en collectiveidea.com

  49. July 01, 2012 at 9:57 AM

    Creo que otros propietarios del sitio debe tener collectiveidea.com como un modelo, muy limpio y el estilo amigable y excelente diseño, y mucho menos el contenido. Usted es un experto en este tema!

  50. September 13, 2012 at 6:10 AM

    Very useful information and I got clear idea about the SSL.

    Thank You,
    Uma Mahesh

  51. unicman@gmail.com
    UnicMan
    December 25, 2012 at 8:37 AM

    That’s quite useful! Thanks!

    BTW have you tried self signed certificates with asset pipline? We are getting issues where none of the js, css, imgs load on a clean browser. On a clean browser, when I hit the self signed https site, it shows security warning. If i accept the warning, it goes ahead but doesn’t load any js/css/imgs.

    What one has to do is - open one of the files in separate tab which shows the security warning again which has to be accepted and then the site starts working fine.

    Let me know if you have found this issue and worked around it somehow.

  52. June 23, 2013 at 7:33 AM

    Teig in gefettete Quiche-Form legen, brigstehenden Teig abschneiden5.

    Ist Nuvagenic wirksam Obwohl es vor kurzem freigegeben wurde, Nuvagenic diätgetränke erweist sich
    als sehr beliebt, dank einer riesigen Menge von Online Werbung in den vergangenen
    Wochen. Nuvagenic ist eine rezeptfreie natrliche diätgetränkepille, die zur Beschleunigung des Stoffwechsels
    und der Eindmmung Heihunger und Hunger hilft. Schinken in Streifen
    schneiden, dazugeben3. Nathans Naturals - Nuvagenic Acai Ergnzung wird auch von einem ziemlich bekannten britischen Firma hergestellt.

  53. shweta_chavan@persistent.co.in
    shweta
    February 12, 2014 at 9:12 AM

    Done all the settings as mentioned,still getting “The page isn’t redirecting properly
    Firefox has detected that the server is redirecting the request for this address in a way that will never complete.” for https.
    And for http getting loop redirections.

    Rails version -3.0.3
    and nginx

    Please suggest