Notes to self

A closer look at Rails force_ssl and assume_ssl

Rails comes with a built-in support for SSL in form of config.force_ssl. But what does it exactly do?

SSL middleware

The force_ssl directive adds the ActionDispatch::SSL middleware layer which is a Rack middleware for HTTPS requests:

# rails/railties/lib/rails/application/default_middleware_stack.rb
...
      def build_stack
        ActionDispatch::MiddlewareStack.new.tap do |middleware|
          if config.force_ssl
            middleware.use ::ActionDispatch::SSL, config.ssl_options
          end
...

This middleware does two main things:

  • SSL/TLS redirect: Redirecting http requests to https with the same URL host and path. Both from the Rails server and the browser by requesting HSTS.

  • Secure cookies: Setting the secure flag on cookies so browsers don’t send out any cookies for plain http requests.

Configuration

The config.force_ssl directive can be set per environment in config/environments/[ENVIRONMENT].rb.

We can adjust the SSL settings with two options, ssl_options and assume_ssl.

The ssl_options directive configures the ActionDispatch::AssumeSSL middleware. We can use it to redirect to a particular host:

config.ssl_options = { redirect: { host: "secure.widgets.com", port: 8080 }

Or exclude healthcheck path from the HTTPS redirect:

config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }

The config.assume_ssl adds additional ActionDispatch::AssumeSSL middleware that will set the following HTTPS headers:

  • HTTPS to on
  • HTTP_X_FORWARDED_PORT to 443
  • HTTP_X_FORWARDED_PROTO to https
  • rack.url_scheme to https

This is useful when running Rails with force_ssl but behind a load balancer or proxy that terminates SSL connection. This prevents ActionDispatch::SSL auto-redirect to HTTPS.

Check out my book
Interested in Ruby on Rails default testing stack? Take Minitest and fixtures for a spin with my latest book.

Get Test Driving Rails while it's in prerelease.

by Josef Strzibny
RSS