Forcing SSL in Phoenix Framework

Article summary

Using SSL (Secure Sockets Layer) in Phoenix Framework is simple. HTTPS encryption is an important step in securing transport-level data, but it also provides a streamlined experience to users in the browser. More browsers are discouraging the use of HTTP, which is good for users and the open web.

Here are a few steps I’ve found to get SSL in Phoenix, redirecting on unencrypted requests, and putting it all together to understand how it works.

Certificates

Hosting providers such as Heroku or Google Cloud Platform provide SSL certificate management by default for paid accounts. This is great, especially when DevOps for more complex security is not necessary.

But securing a site is only half of the issue. If a user is going directly to a URL without specifying a protocol, the request will default to HTTP, which can result in unwanted risks.

Forcing SSL

SSL can be configured from the config/prod.exs script file. In fact, there’s already a commented-out solution in the generated prod.exs file. The code below is the most straightforward implementation of forcing SSL for deployment with Distillery.

config :app_name, AppNameWeb.Endpoint
# ...
url: [host: "example.com", port: 443],
http: [port: "${PORT}"],
force_ssl: [hsts: true],
# ...

It is worth reviewing what these lines of code mean. SSL is enabled by the Plug.SSL module. The options that can be passed to the force_ssl configuration are dictated by the same options available to Plug.SSL.

There are three options worth considering. Each of them is discussed in an Elixir Forum post, which provides some helpful perspective on different configurations.

hsts

The hsts option sets the Strict-Transport-Security header, which specifies that the browser should only access a site through HTTPS. The option defaults the max-age, preload, and subdomains directives for the header, but you can also specify these in the production config script. The source code points to further documentation on the preload directive.

host

The host option is provided in case the new URL host does not match the one provided by conn.host. As long as the HTTPS domain matches the HTTP one, you do not need to specify the host option.

rewrite_on

The importance of the rewrite_on header depends on the hosting provider. For instance, Google Cloud Platform strips HTTPS requests and forwards the request from the load balancer in HTTP.

This is one of the three reasons why Plug provides the rewrite_on option, and why it should be set to rewrite_on: [:x_forwarded_proto], where the proxy sets the X-Forwarded-Proto header and sends it to Plug. If the application is behind a proxy and the x-forwarded-proto option is not set, the browser may get stuck in a redirect loop.

SSL in Phoenix is pretty straightfoward, and a review of the documentation and community feedback makes the extensibility of the feature clear. While several of the options are mandatory to get off the ground, there are plenty more to match the needs of a modern web application. If the future of a secure web is HTTPS, Phoenix is ready.