Force HTTPS for AWS EB behind a Load Balancer

Feb 8, 2020

Elastic Beanstalk is great! It is very easy to get a Rails app up and running on AWS quickly. Perfect for an app developer that prefers not to deal directly with sysops but still wants to deploy to and leverage the services on AWS.

Sooner or later though, you will probably run into something that you can't fix through the web console GUI.

HTTP to HTTPS redirect

It is straight forward to set up your listeners and your processes for the Elastic Load Balancer (ELB) to route the requests to your app, both with and without SSL. But there is no configurable option to redirect all HTTP requests to HTTPS, meaning you have to handle it in your app.

In Rails, you could force HTTPS redirect by adding force_ssl in ApplicationController, but it seems a bit unnecessary. Do we really need the request to go that far, just to be redirected? Plus, should this security setting really be up to the application?

EB Extensions to the rescue

A few years ago, I found the article Enable HTTPS redirect for AWS Elastic Beanstalk with a Rails App behind a Load Balancer, where this issue is addressed. I found it to be a good resource, but unfortunately, the link to the sample file is now broken. The Github repository has been updated and restructured since that article was written, but the folder containing the updated sample files can currently be found on https://github.com/awsdocs/elastic-beanstalk-samples. In case they change it again in the future, I have forked the current state so you can also find those files on https://github.com/dannemanne/elastic-beanstalk-samples

In that repo, there are two files. One is an extension file for Elastic Beanstalk, meant to be placed in the .ebextensions folder of your app source code. This extension creates a customized NginX configuration file, which has the following modifications:

set $redirect 0;
location / {
  if ($http_x_forwarded_proto != "https") {
    set $redirect 1;
  }
  if ($http_user_agent ~* "ELB-HealthChecker") {
    set $redirect 0;
  }
  if ($redirect = 1) {
    return 301 https://$host$request_uri;
  }
  passenger_enabled on;
}

And with the repository being updated, the example also contains those same redirect rules for the asset pipeline location.

Rails asset pipeline support.

location ~ "^/assets/.+-[0-9a-f]{32}\..+" {
  if ($http_x_forwarded_proto != "https") {
    set $redirect 1;
  }
  if ($http_user_agent ~* "ELB-HealthChecker") {
    set $redirect 0;
  }
  if ($redirect = 1) {
    return 301 https://$host$request_uri;
  }

  error_page 490 = @static_asset;
  error_page 491 = @dynamic_request;
  recursive_error_pages on;

  if (-f $request_filename) {
    return 490;
  }
  if (!-f $request_filename) {
    return 491;
  }
}

Looking at the commit history, this seems to be a more recent improvement.

Configure Phusion Passenger

The other file in the example is a JSON file (passenger-config.json). This file is meant to be saved in the root of your app source code. Doing so allows you to pass options to Phusion Passenger as it boots.

Note! The filename passenger-config.json is correct for Passenger Phusion version 4, which is what Elastic Beanstalk is currently using. If/when they switch to version 5, the config filename will be Passengerfile.json, but that is not needed at the time of this article.

The passenger-config file in this example is needed because the extension is not replacing the original NginX configuration file that Elastic Beanstalk uses by default. It creates a new file in another location. The passenger config file can tell Passenger to use this new NginX config file rather than the default, which is exactly what it does in this example.

Alternative approach

If you prefer to keep the configurations to a minimum, you could replace the original NginX config file (which is located in /opt/elasticbeanstalk/support/conf/nginx_config.erb), but I would recommend going with the approach from the example. If not, then just imagine what would happen when it is time to upgrade to another version of the server image. This updated version might contain changes to the configuration, but you would never know because it is immediately overwritten during deployment.

If instead, you wrote to a custom location, you could (and should) compare the files for possible changes. It's just good practice!

Happy coding!

Also posted on dev.to

Resources

Enable HTTPS redirect for AWS Elastic Beanstalk with a Rails App behind a Load Balancer

https://github.com/awsdocs/elastic-beanstalk-samples

Advanced environment customization with configuration files (.ebextensions)

Introduction to configuring Passenger Standalone