Proper way to enforce https only?

I stumbled upon a similar problem with a Phoenix application deployed in AWS Elastic Beanstalk with a load balancer and the force_ssl: [rewrite_on: [:x_forwarded_proto] setting fixed the too many redirects errors. However, as the Plug.SSL documentation mentions:

Since rewriting the scheme based on x-forwarded-proto can open up security vulnerabilities, only provide the option above if:

    your app is behind a proxy
    your proxy strips x-forwarded-proto headers from all incoming requests
    your proxy sets the x-forwarded-proto and sends it to Plug

I wanted to ask to be sure, but are there any security vulnerabilities in this case with an AWS LB? The AWS Classic Load Balancer documentations says:

Elastic Load Balancing stores the protocol used between the client and the load balancer in the X-Forwarded-Proto request header and passes the header along to your server. 

So I assume the third case from the Plug.SSL docs (your proxy sets the x-forwarded-proto and sends it to Plug) happens in this case, am I correct to assume that and that this setting should be fine from a security perspective here?

In the context of localhost for dev nothing from above will allow me to redirect all http requests to https.
What should I do? Is this just a bad idea? I feel crazy having this hard of a time trying figuring it out.

Edit: After some more digging and help from @NobbZ I found that I needed a combination of host, exclude and rewrite_on to make this work locally. Also leaving host as nil would cause 4000 to be used when redirecting to https.

1 Like

I’m running into the same issue with GCP. I have a simple Phoenix app running on App Engine, but it’s not redirecting to https. I’ve tried all recommendations above, but nothing has worked for me.

Here is what I currently have in my config/releases.ex:

import Config

config :my_app, force_ssl: true

config :my_app, MyAppWeb.Endpoint,
  load_from_system_env: true,
  check_origin: false,
  server: true,
  root: ".",
  url: [scheme: "https", port: 443],
  cache_static_manifest: "priv/static/cache_manifest.json",
  force_ssl: [rewrite_on: [:x_forwarded_proto], hsts: true, host: nil]

When I run curl -s -D- http://my-app.com I get a 200 and the html response back, and when I run curl -s -D- https://my-app.com | grep -i Strict and get no output at all.

Can anyone help?

1 Like

Nginx.

Set :force_ssl in your config/prod.exs and not config/releases.exs, as it is read at compile time. More recent Phoenix versions actually warn if you do this mistake.

8 Likes

Thanks @josevalim! Got it working!

1 Like

I’m using Google App Engine Flex and the above is working fine for me.

HTTP traffic redirects to HTTPS nicely and my Elixir app is only configured for HTTP (since GCP manages my certificate and handles that outside of my app).