25
votes

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?

1
Are you returning http:// links to the client?arx
I cannot find one. All the paths in the client are relative (for ajax calls).Greg
Most browsers have some means of tracing all the calls being made from your code, monitoring redirections, etc. I'd run a trace and see where the http links are coming from (e.g. either from your code or redirections from the far end).arx
I think the root cause is the way that express does redirects. Unless the node server was created with HTTPS, redirects default to HTTP. I can't find that in the docs anywhere, but it is certainly behaving that way. I need to find a way to do a rewrite of the header.Greg

1 Answers

59
votes

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!