Redirect HTTP to HTTPS in AWS Elastic Beanstalk

There are many ways to redirect incoming HTTP requests to HTTPS in Beanstalk. This article explores a few, with pros & cons of each. But first, the prerequisites:

Prerequisites

Before we get to Beanstalk itself, there are a few things to take care of, to make HTTPS work. The rest of this article assumes that you have already:

  • Pointed your domain to the Beanstalk environment using either Route 53 or any other DNS provider.
  • Created listeners to listen on ports 80 & 443 of your Beanstalk environment’s load balancer.
  • Properly set up traffic forwarding from the LB’s listeners to the correct ports on the intended target groups & instances.
  • Configured the LB’s HTTPS listener with a valid TLS certificate.
  • Opened the necessary ports in all security groups involved.

The Ideal Solution

Beanstalk being a PaaS offering, the ideal solution here would be one that uses features provided by Beanstalk itself to accomplish this requirement, instead of solutions that try to circumvent Beanstalk’s architecture & make direct changes to the underlying server configuration or file system.

This article is for Beanstalk environments that use the Apache server. The solution would differ for Nginx & Docker-based deployments. For Apache, create a file named .ebextensions/*.config in your deployment package:

files:
  "/etc/httpd/conf.d/ssl_rewrite.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      RewriteEngine On
      <If "-n '%{HTTP:X-Forwarded-Proto}' && %{HTTP:X-Forwarded-Proto} != 'https'">
        RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
      </If>

Let us understand how this works. If you were modifying Apache config files directly, you would add an Apache rewrite rule like:

RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

or in case you’re behind a load balancer:

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

These only work in a <VirtualHost> block. Changing the RewriteCond to an <If> block makes it work outside a <VirtualHost> block. This also means that we can put it in a standalone Apache config file. Apache by default (on CentOS & Beanstalk) picks up config from /etc/httpd/conf.d/*.conf so that’s where we put our config too.

-n '%{HTTP:X-Forwarded-Proto}' prevents redirection if you’re not behind a load balancer, so you can deploy this config unchanged to environments with & without a load balancer. For example, if you have a load balancer in production but are using a single instance for dev/test.

If you’re using old Apache < v2.4, use this instead:

files:
  "/etc/httpd/conf.d/ssl_rewrite.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      LoadModule rewrite_module modules/mod_rewrite.so
      RewriteEngine On
      RewriteCond %{HTTPS} !=on
      RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]

And for Python:

files:
  "/etc/httpd/conf.d/https_redirect.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      <Directory /opt/python/current/app/>
      RewriteEngine on
      RewriteCond %{HTTP:X-Forwarded-Proto} ^http$
      RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
      </Directory>

Other (Subpar) Solutions

You can create a CloudFront distribution in front of your Beanstalk environment & configure CloudFront to redirect HTTP to HTTPS. This is fine if you really need a CDN & the advantages that a CDN provides, but using CloudFront just for this redirection is definitely overkill. Do remember to either disable the CloudFront cache or set it up appropriately if you need to go this way.

You can always SSH directly into any of the EC2 instances that make up the Beanstalk environment & make changes directly either to Apache config or other files to accomplish this. But of course, this is going to be a short-lived solution. You lose the config when:

  • The autoscaling group recreates instances.
  • You clone the environment.
  • You make certain environment configuration changes from the Beanstalk console.

You could also use the .ebextensions method to overwrite the entire Apache config, but if you do, you could be fighting the system in a way. Beanstalk will evolve & change over time & if you always blindly overwrite the config, you will end up in trouble sooner or later.

Yet another way is to programmatically inject a few lines of config in Apache config during deployment, by using a shell script perhaps. This’ll work, except that the file you’re targeting might not be the right one in future versions of Beanstalk. Another issue is that Beanstalk tends to rewrite/overwrite certain config files sometimes. If one of those contains your custom config, you just lost it!

References