6

I am using the following to redirect all http requests to https requests.

I can see from logs that the header 'x-forwarded-proto' is never populated and is undefined.

app.get('*', function(req, res, next) {
    //http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-proto
    if (req.headers['x-forwarded-proto'] != "https") {
        res.redirect('https://' + req.get('host') + req.url);
    } else {
        next();     
    }
});

It is causing a redirect loop. How can I redirect properly without looping?

user883499
  • 468
  • 1
  • 7
  • 16

2 Answers2

10

edit: my original answer below is for express 3.x, for 4.x you can get a string http or https in req.protocol, thx @BrandonClark


use req.get, not req.headers. Note that POST requests and all other non-GET will not see this middleware. It's also possible that Express does not carry the x-forwarded-proto header across when you redirect. You may need to set it yourself.

app.get('*', function(req, res, next) {
//http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-proto
    if (req.get('x-forwarded-proto') != "https") {
        res.set('x-forwarded-proto', 'https');
        res.redirect('https://' + req.get('host') + req.url);
    } else {
        next();     
    }
});

Another way to force https:

function ensureSecure(req, res, next){
  if(req.secure){
    // OK, continue
    return next();
  };
  res.redirect('https://'+req.host+req.url); // handle port numbers if non 443
};

app.all('*', ensureSecure);
Community
  • 1
  • 1
Plato
  • 10,812
  • 2
  • 41
  • 61
  • Tried your suggestions and still not working. Elastic Load balancer is not setting the proper headers. `res.set('x-forwarded-proto', 'https');` does not work as well. – user883499 Sep 27 '13 at 13:04
  • Figure out why Amazon isn't setting the header, or try my second technique. I noticed that `res.header` is identical to `res.set`, sorry! – Plato Sep 27 '13 at 13:09
  • 1
    Second technique would also not work as load balancer gives a http (req.secure always false) request always to the app. – user883499 Sep 27 '13 at 13:12
  • Please explain more about what you're doing. Do you have other requests coming in besides requests from the Amazon elastic load balancer? If the ELB is making http requests to your app, and you want https, can't you configure the ELB to make https requests? – Plato Sep 27 '13 at 13:22
  • No https requests are taken by the load balancer and that is the end point for https. After that it is all http inside app and all that. – user883499 Sep 27 '13 at 13:24
  • I see, well, sounds like you are stuck until you figure out what's happening to the header. Try a curl request and see if you see it there; if so maybe Express is breaking it; if not Amazon is probably misconfigured – Plato Sep 27 '13 at 13:26
  • 3
    Express 4 you can use `req.protocol` to detect `http` or `https`. – Brandon Clark Oct 28 '14 at 11:29
  • or you can check `req.secure` (4.x) which is a short-hand for `'https' == req.protocol;` – Claudiu Hojda Mar 29 '16 at 15:24
  • 1
    great tip for AWS elb and alb – Gaurav Rawat Jun 27 '17 at 21:28
  • 1
    I only got this working by doing the following `router.use(function(req, res, next) { if (req.header('X-Forwarded-Proto') == 'https') next(); else res.redirect('https://' + req.host + req.url); });` more details about the X-Forwarded-Proto header can be found here https://aws.amazon.com/premiumsupport/knowledge-center/redirect-http-https-elb/ – DauleDK Oct 18 '17 at 19:13
1

You can edit the nginx config file in the EC2 instance. SSH to ec2 instance and follow the following steps

  1. go to /etc/nginx/conf.d
  2. open 00_elastic_beanstalk_proxy.conf sudo vi 00_elastic_beanstalk_proxy.conf
  3. put

    location / { if ($http_x_forwarded_proto != 'https') { rewrite ^ https://$host$request_uri? permanent; } … }

  4. reload nginx sudo /usr/sbin/nginx -s reload

Kamrul
  • 7,175
  • 3
  • 31
  • 31