Force HTTPS behind Elastic Load Balancer on a NodeJS application

HTTPS is good. We all know this. So how to ensure only HTTPS traffic for your Express application that is hosted behind an Elastic Load Balancer?

HTTPS benefits

If you have not enabled HTTPS for your website yet, it is about time to do so. The benefits with HTTPS are just so incredibly good!

  • Encryption for the end user
  • Service worker can be enabled for most browsers, which is a must-have for Progressive Web Apps (PWA) 
  • Increased Google page rank
  • Improved loading performance, if your webserver support HTTP2
  • Cool browser label in the address bar which increases customer confidence

There are a lot of guides on how to do it, and getting a certificate is actually free if you use AWS Certifiate Manager, or other free Certificate Authorities like LetsEncrypt

Forcing HTTPS on Express behind Elastic Load Balancer

Our stack uses Express on a NodeJS server, so this blog post is only focusing on Express. AWS have fine guides on how to redirect for other web servers, but none on Express unfortunatly.

Since Elastic Load Balancer forwards all traffic (both http and https) to the same port in our application, all the traffic internally in our application will be http. This is exactly what we want, but we must force the end user to only use https.

Elastic Load Balancer is nice enough to append an extra request header to the request (x-forwarded-proto) which tells us from which protocol the request originated from. We can use this in an Express middleware to do redirections:

function forceHttps(req, res, next) {
   const xfp =
     req.headers["X-Forwarded-Proto"] || req.headers["x-forwarded-proto"];
   if (xfp === "http") {
     res.redirect(301, `https://${hostname}${req.url}`);
   } else {
     next();
   }
}

server.use(forceHttps);

NPM module

Since we don't want to write the middleware again and again for all our applications, we created a small NPM module that does just that.

This is how you use it:

const express = require('express');
const forceHttps = require('@crystallize/elasticloadbalancer-express-force-https');

const server = express();
server.use(forceHttps());

That's it! All your traffic will now be forwarded to https if the x-forwarded-proto=http. It will not do anything if the header is not present.