How to force node.js/express.js to HTTPS when it is running behind an AWS load balancer

Greg picture Greg · Jan 26, 2012 · Viewed 9.5k times · Source

I'm running a node/express service on AWS and have deployed an ELB in front of it. When I spin up an ELB instance with SSL enabled, it works for the first page I hit, but then switches over to HTTP for every server access after that.

The routing rule on the ELB terminates the SSL and forwards to port 8080 which node is listening on.

The SSL termination solution will work fine for my purposes, but how can I keep subsequent server calls on HTTPS?

Answer

Kevin. picture Kevin. · Jul 13, 2013

I have experienced the same issue, but in a slightly different context. I was deploying Node.js/Express application using the AWS Elastic Beanstalk and was able to install an SSL certificate on it.

The result of this was that my application was accessible on both the http and https protocol. The routing table of the load balancer were looking like this :

(Load balancer) http 80 --> (Node instance) http 8080
(Load balancer) https 443 --> (Node instance) http 8080

So the question was to authorize only https connection on my node.js app, but enabling redirection to https if the connection was done initialy using http.

Because behind the AWS load balancer, all the communication are done over http, a global redirection instruction (as a middleware in this case) like this one would create an infinite redirection loop:

app.use(function(req, res, next) {
    if((!req.secure) && (req.protocol !== 'https')) {
        res.redirect('https://' + req.get('Host') + req.url);
    }
}

--> simply becaue the instruction (req.protocol !== 'https') would always be true!

From this blog post (http://matthew.mceachen.us/blog/howto-force-https-with-amazon-elastic-load-balancer-and-apache-1071.html), it turns out that the AWS ELB adds a X-Forwarded-Proto header that you can capture to know what was the protocol used before the load balancer (http or https).

So this small modification did the trick :

app.use(function(req, res, next) {
    if((!req.secure) && (req.get('X-Forwarded-Proto') !== 'https')) {
        res.redirect('https://' + req.get('Host') + req.url);
    }
    else
        next();
});

Hope this help!