12

I have installed a SSL certificate on my page, which runs a Node.js + Express application, configured the Express server to always force redirection to HTTPs and everything works fine, except that the https redirection only works when the page is reloaded or when the Enter key is pressed again. I recorded a gif to show what happens:

http://recordit.co/uBiW3bcQCM

And here's my Express configuration.

var express = require('express');
var path = require('path');
var serveStatic = require('serve-static');

var forceSsl = function (req, res, next) {
  if (req.headers['x-forwarded-proto'] !== 'https') {
    return res.redirect(['https://', req.get('Host'), req.url].join(''));
  }
  return next();
};

app = express();
app.use(serveStatic(__dirname));

if(process.env.NODE_ENV === 'production') {
  app.use(forceSsl);
}

app.all('/*', function(req, res) {
  res.sendfile('index.html');
});

var port = process.env.PORT || 5000;
app.listen(port);

console.log('server started '+ port);

My application runs on Heroku. Can anyone help me finding out what's happening?

Thanks in advance.

felipeecst
  • 1,355
  • 3
  • 16
  • 32

2 Answers2

7

I think your server is sending the content before sending the redirection headers.

If you swap:

app.use(serveStatic(__dirname));
app.use(forceSsl);

for:

app.use(forceSsl);
app.use(serveStatic(__dirname));

It seems to work much better!

The reason why your browser did perform the redirection on reload/enter is obscure to me as I could not reproduce the behavior. On FF I was never redirected.

It might be due to the request headers being different, such as a HEAD, instead of a GET, or something else. I could not investigate more on that, use Wireshark or Burpsuite to know exactly what happens, if that still matters...

Fabien
  • 4,862
  • 2
  • 19
  • 33
2

This redirectToHTTPS() middleware should work for you. It will redirect to https site even when the user does not provide prefix. Add X-Forwarded-Port for identifying the port used for https site.

function redirectToHTTPS () {
  return function middlewareRedirectToHTTPS (req, res, next) {
    const isNotSecure = (!req.get('x-forwarded-port') && req.protocol !== 'https') ||
      parseInt(req.get('x-forwarded-port'), 10) !== 443
    if (isNotSecure) {
      return res.redirect('https://' + req.get('host') + req.url)
    }
    next()
}
}
Bharathvaj Ganesan
  • 3,054
  • 1
  • 18
  • 32
  • Do I have to change anything else in my configuration? Unfortunately, only replacing the function didn't work :/ I'm setting like this: – felipeecst Aug 02 '17 at 16:24
  • `if(process.env.NODE_ENV === 'production') { app.use(redirectToHTTPS()); }` – felipeecst Aug 02 '17 at 16:24
  • Try this https://www.npmjs.com/package/express-http-to-https nodemodule. – Bharathvaj Ganesan Aug 02 '17 at 16:48
  • @felipeecst did u listen to port 443 for https. Check out this answer https://stackoverflow.com/questions/22453782/nodejs-http-and-https-over-same-port?noredirect=1&lq=1 ```var httpServer = http.createServer(app); var httpsServer = https.createServer(credentials, app); httpServer.listen(8080); httpsServer.listen(8443); ``` – Bharathvaj Ganesan Aug 02 '17 at 16:56