Rails SSL from Development to Deploy

If you're accepting payments, integrating with social networks or logging in users with a password you should probably be using SSL.

However, it's not as easy just slapping a cert on your site and getting back to Hackernews.

Modern web applications are built on a mix of services (hosting, content networks, third party analytics and monitoring, etc.) and it is vital to check that they can deliver their content to you over SSL. Something it's much better to know in development than when you do your first production deploy.

To be truly effective, and to keep yourself from having to say: "But it worked on my dev server!" you need to be using SSL, and using it correctly from development to deploy.

Also, presumably, you don't want to just "use SSL" but to actually take the steps to really harden your application security.

Development Environment

Most developers don't use SSL locally as OH MY GOD IT CAN BE SUCH A (#)$@#)ADSF PAIN to setup. However, using SSL in development is vital to let you find all sorts of issues and problems before you push your code into production.

Use GetForward for SSL

While you can setup a self-signed certificate, the rise of 'localhost forwarding' services and applications offers an easier way to get going with SSL.

The different services all work similarly to a SSH tunnel, where incoming traffic on a unique URL is assigned back to your local machine and forwarded to the port your development server is running on.

The service I've had the most luck with is ForwardHQ. Their client installs as a gem and has one of the nicest command line setups I've come across.

After installing, you can invoke the SSL secured tunnel by launching

forward 3000 example

Which would present your locally running Rails server (running on default port 3000) on:


Secured Development

An important aspect of these services is that they do actually put your local machine (in dev state) onto the full internet (good for collaboration, not so much for security), so it's prudent to make sure you aren't exposing data that you didn't intend.

A very lightweight method of securing your whole site is to drop in a hard coded HTTP Basic Auth challenge that only kicks in for development mode.

Place the following in your application controller and set a username/password combo in your .env file (presuming that you're using the dotenv gem).

def basic_auth
	if Rails.env.development?							
	    unless session[:basic_authed]
	        authenticate_or_request_with_http_basic do |username, password|
	            username == ENV['BASIC_USER'] && password == ENV['BASIC_PASSWORD']
	            session[:basic_authed] = true

Mixed Content Issues

"somebody hardcoded a http link somewhere"

Depending on the browser you're using, inadvertendly referencing plain 'http' content on a page being served as 'https' can trigger anything from a subtle alert icon to error messages that all but assure the user that Estonian hackers are rifling through their credit card numbers as they read them.

Example IE9 Error
Ie9 secure content

Asset Pipeline Awesomeness

If you're exclusively using the Rails Asset Pipeline and serving your static assets directly from your app server everything should just work as, by default, static files are referenced relatively and served from the public/assets directory in production.


<script src="/assets/application-92c4828342c9301dbd7232fdc46fd71c.js"></script>

Direct Non-Pipeline Includes

However, there are still lots of cases where you might not be able to do take advantage of the asset pipeline: multimedia embeds, analytics scripts and conversion testing tools may require extra steps to setup on a SSL site.

While you can certainly hardcode all the referenced links to force ssl (which is a completely valid option). Modern browsers also support a protocol-less option:

<img src='//corporate-server.com/images/logo.png'>

	instead of

<img src='http://corporate-server.com/images/logo.png'>

Mixed Scripting Warnings

More subtle, but just as worrisome to the user, mixed scripting errors occur when a javascript action references a resource that is not being served over SSL.

Frighteningly these can occur from almost anything you include into your site (ex: until recently the Youtube player embed caused them when a video began playing).

Frustratingly, they are also often triggered from users with buggy browser extensions enabled, who then blame your site for improper behavior.

Tools for Troubleshooting

1. Firebug - https://getfirebug.com/

Firebug 1.9 added a Protocol column to the Net Panel which allows for easy sorting of all requests and resources included on the page.

Install Firebug, Load your Page and then sort by protocol to find any 'http' requests.

Firebug net protocol

2. Fiddler - http://www.telerik.com/fiddler

Fiddler is a web debugging proxy tool for developers which offers a number of different methods of diagnosing mixed content errors

Fiddler protocol

Application Setup

Rails has some great built in convenience features for working with SSL. But before you enable those you need to decide if your application is going to be covered fully by SSL or if you'll pick and choose pages and forms to secure.

It's important to really consider this as it can be difficult (or impossible) to shift back without presenting frightening errors to site visitors.

If you're ready to bite the bullet and use SSL for your entire application, you can add the following command to your application.rb (This presumes you're going to be doing SSL in development as described above. Otherwise, place this command in production.rb)

config.force_ssl = true

This adds the Rack SSL middleware to your Rails app which enables three key security features for us:

1. HTTP to HTTPS Redirection

Requests to plain http pages will be automatically 301 redirected to the https protocol for them. A 301 is a permanent redirect, so the responses should be cached by the client browsers and there should be no effect on any search engine rankings.

2. Secure Cookies by Default

Developers often think of cookies as if they are some magic persistence mechanism that teleports settings from server to browser and don't consider that in actual fact they pass between browser and server just as form fields do.

Given that, it becomes very clear that you need to set cookies to only function under SSL as well.

Manually this is done by setting the 'secure' flag to true when you first create a cookie.

cookies[:user_name, secure: true] = "abagail"

This is done automatically when force_ssl is enabled.

3. HSTS, the HTTP Strict Transport Security

A relative newcomer to the web security landscape, HSTS is a means of declaring that a web browser should only communicate with it over SSL.

The server sends a HTTP response header named "Strict-Transport-Security", which forces client connections to flip from 'http' to 'https' before they can access the server.

HSTS was developed in response to Firesheep / SSL Stripping attacks. These type of man-in-the-middle attacks would prevent a visitor from being able to determine if a site was supposed to be using SSL or not.

The header is read by modern browsers, which will present an error message if a HSTS serving site is communicated with over a non SSL connection.

When a HSTS connection is set, an expires value (max-age) is set as well. An extremely important point to remember is that if a browser visits a HTTPS page on a site and then tries to access another non-HTTPS page during the expiry period they will receive an error, making HSTS unsuitable for mixed HTTP/HTTPS sites.

Production Deployment

Content Delivery Networks

However, serving images, javascripts and stylesheets directly from your Heroku dyno can really hurt performance. So you should ideally be serving all assets from a Content Delivery Network (CDN) such as Amazon's Cloudfront.

See for detailed setup instructions https://devcenter.heroku.com/articles/using-amazon-cloudfront-cdn

While this recommendation is suggested for any Heroku app, if you're going to have most of your app served over SSL you'll see a larger benefit than normal as well.