How to Force HTTPS Redirect on AWS Elastic Beanstalk with Load Balancer (Docker/Python 3.4)


2 views

When dealing with Elastic Beanstalk environments that use Load Balancers (which is most production setups), there's a critical architectural detail many developers miss: the SSL termination happens at the LB level. This means your EC2 instances receive HTTP traffic even when the original request was HTTPS.

The standard Apache mod_rewrite approach fails because:


# This WON'T work in EB with ALB:
RewriteEngine On 
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

The %{HTTPS} variable always shows "off" since the LB already terminated SSL.

For Docker-based Python environments (specifically Debian Jessie), create this .ebextensions/https-redirect.config:


files:
  "/etc/nginx/conf.d/https_redirect.conf":
    content: |
      server {
        listen 80;
        if ($http_x_forwarded_proto = "http") {
          return 301 https://$host$request_uri;
        }
      }
container_commands:
  01_restart_nginx:
    command: "service nginx restart"

If you're using Apache instead of Nginx, use this in .ebextensions/securelistener.config:


files:
  "/etc/httpd/conf.d/ssl_rewrite.conf":
    content: |
      RewriteEngine On
      RewriteCond %{HTTP:X-Forwarded-Proto} =http
      RewriteRule .* https://%{HTTP:Host}%{REQUEST_URI} [L,R=permanent]

After deployment, verify with:


curl -I -H "X-Forwarded-Proto: http" http://yourdomain.com

Should return 301 with Location header pointing to HTTPS.

  • Double redirects: Ensure your ALB listener rules don't conflict
  • Mixed content: Update all absolute URLs in your Ember app to use https://
  • Health checks: Whitelist the ELB health check path from redirects

Since you're using Elastic Beanstalk with a load balancer, the most reliable method is to leverage the X-Forwarded-Proto header that AWS ALB automatically injects. Here's how to implement this in your Docker/Python environment:

# .ebextensions/01_https_redirect.config
files:
  "/etc/nginx/conf.d/https_redirect.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      server {
        listen 80;
        if ($http_x_forwarded_proto = "http") {
          return 301 https://$host$request_uri;
        }
      }
container_commands:
  01_reload_nginx:
    command: "service nginx reload"

If you prefer handling this at the application level (especially useful for Python WSGI apps), add this middleware:

# https_redirect_middleware.py
from django.http import HttpResponsePermanentRedirect

class HTTPSRedirectMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        if 'HTTP_X_FORWARDED_PROTO' in request.META:
            if request.META['HTTP_X_FORWARDED_PROTO'] == 'http':
                return HttpResponsePermanentRedirect(
                    'https://' + request.get_host() + request.path
                )
        return self.get_response(request)

When this doesn't work, check these troubleshooting steps:

  • Verify ALB listeners: Ensure both HTTP (port 80) and HTTPS (port 443) listeners exist
  • Confirm security groups: The instance must allow HTTP traffic from the ALB
  • Check nginx logs: sudo tail -f /var/log/nginx/access.log

For Docker containers, you'll need to modify the nginx config inside the container:

# Dockerfile
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf

With corresponding nginx config:

# nginx.conf
server {
    listen 80;
    server_name _;
    
    if ($http_x_forwarded_proto != "https") {
        return 301 https://$host$request_uri;
    }
    
    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}