Frank Mitchell

Forcing HTTPS on in Amazon's EC2

It’s pretty common these days to see HTTP connections redirected to HTTPS. If you’re running servers in Amazon’s EC2, you can configure an Elastic Load Balancer to hanlde the SSL termination. But to handle the redirect, you need a proxy. Here are configurations for three popular ones.

HAProxy

HAProxy won’t handle serving up your web site, but it will handle routing your traffic. Confiure your ELB to pass both HTTP and HTTPS traffic on to your web server as HTTP traffic on port 80.

ELB Protocol ELB Port Server Protocol Server Port
HTTPS443 HTTP80
HTTP80 HTTP80

Amazon’s ELB will set the X-Forwarded-Proto header based on the original connection. The header’s value is “http” if the connection came in on port 80, and “https” if it came in on port 443. You can use an acl definition and redirect command to detect an incomming HTTP connection and redirect it to a HTTPS one.

acl is_http hdr(X-Forwarded-Proto) http
redirect proxy https://domain.com 301 if is_http

Passing the proxy flag to the redirect command keeps the path and query arguments on the URL intact. Specifying a 301 return code tells visitors this is a permanent redirect.

Nginx

Denis Klykvin showed me how to use the $http_x_forwarded_proto variable for header testing in Nginx.

if ($header_x_forwarded_proto) {
  rewrite https://domain.com/$request_uri permanent;
}

However, Nginx thinks if is evil. Instead, take advantage of the default difference in ports for HTTP and HTTPS traffic. Configure your ELB to pass HTTP traffic to your web server on port 80 and HTTPS traffic to your web server on port 443.

ELB Protocol ELB Port Server Protocol Server Port
HTTPS443 HTTP443
HTTP80 HTTP80

You can use server blocks to listen on both ports. If you get a connection on port 80, you know it started as a HTTP connection, so you can redirect it to a HTTPS one.

server {
  listen 80;
  return 301 https://domain.com/$request_uri;
}

server {
  listen 443;
}

The $request_uri variable keeps the path and query arguments on the URL intact. Specifying a 301 return code tells visitors this is a permanent redirect.

Apache

Apache can detect the X-Forwarded-Proto header the ELB sets and use it to redirect traffic. Confiure your ELB to pass both HTTP and HTTPS traffic on to your web server as HTTP traffic on port 80.

ELB Protocol ELB Port Server Protocol Server Port
HTTPS443 HTTP80
HTTP80 HTTP80

Set up a mod_rewrite rule to check the X-Forwarded-Proto header. If it comes in with a value of “http”, you can redirect that traffic to HTTPS.

<VirutalHost *:80>
  RewriteEngine On
  RewriteCond %{HTTP:X-Forwarded-Proto} http
  RewriteRule https://domain.com%{REQUEST_URI} [L,R=301]
</VirtualHost>

The %{REQUEST_URI} variable keeps the path and query arguments on the URL intact. Specifying R=301 flag causes Apache to return a 301 response code, which tells visitors this is a permanent redirect.

Final thoughts

There are a host of other ways to configure your web servers as well. HAProxy and Apache can support Nginx’s “listen on two ports” setup, and Nginx does have if conditions you can use to check headers. How you get your HTTP traffic redirected to HTTPS does’t really matter. What counts is that you’re providing a safer experience for your users.