Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.JS, Express and Heroku - how to handle HTTP and HTTPS?

I have an app which is quite normal Express app - simple server logic, views, lots of client-side JS. I have to do many AJAX requests. Some of them need to be secured by HTTPS protocol (some needn't).

So, my server should work with both HTTP and HTTPS. It should also work o both the local machine (ran with nodemon normally) and on Heroku.

As far as I understood, Heroku gives you a single port (process.env.PORT) you can listen to, and handles all requests through the proxy (so, you app is listening to this port and not bothering about the proto - right?)

So, am I getting this right - I should have some different code for dev machine and Heroku?

Like

...
app = express()
...

if process.env.NODE_ENV == 'production'
  app.listen(process.env.PORT)
else
  https = require('https')
  http = require('http')
  http.createServer(app).listen(5080) # some local port
  options = {
    key: fs.readFileSync('key.pem'), 
    cert: fs.readFileSync('cert.pem') # my self-signed files
  }
  https.createServer(options, app).listen(5443) # some different local port

Is it the proper way to deal with this?

like image 209
Guard Avatar asked Nov 01 '12 21:11

Guard


People also ask

How do I change from HTTP to HTTPS in Express?

We will perform HTTP to HTTPS redirection by creating an Express middleware function [ 1] and then, inside that function, write the redirection code that will force Express to use HTTPS. The middleware function provides access to the Express req and res objects and next function that we will need.


3 Answers

For the Coffeescript-challenges, here is a version of Guard's answer converted to Javascript. I took a different approach to splitting up the if else statements.

var express = require('express');
var http = require('http');
var https = require('https');
var fs = require('fs');
var privateKey = fs.readFileSync('./config/localhost.key').toString();
var certificate = fs.readFileSync('./config/localhost.crt').toString();

var options = {
  key : privateKey
, cert : certificate
}

var app = express();

// Start server.
var port = process.env.PORT || 3000; // Used by Heroku and http on localhost
process.env['PORT'] = process.env.PORT || 4000; // Used by https on localhost

http.createServer(app).listen(port, function () {
    console.log("Express server listening on port %d in %s mode", this.address().port, app.settings.env);
});

// Run separate https server if on localhost
if (process.env.NODE_ENV != 'production') {
    https.createServer(options, app).listen(process.env.PORT, function () {
        console.log("Express server listening with https on port %d in %s mode", this.address().port, app.settings.env);
    });
};

if (process.env.NODE_ENV == 'production') {
    app.use(function (req, res, next) {
        res.setHeader('Strict-Transport-Security', 'max-age=8640000; includeSubDomains');
        if (req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'] === "http") {
            return res.redirect(301, 'https://' + req.host + req.url);
        } else {
            return next();
            }
    });
} else {
    app.use(function (req, res, next) {
        res.setHeader('Strict-Transport-Security', 'max-age=8640000; includeSubDomains');
        if (!req.secure) {
            return res.redirect(301, 'https://' + req.host  + ":" + process.env.PORT + req.url);
        } else {
            return next();
            }
    });

};
like image 140
Dan Kohn Avatar answered Oct 20 '22 00:10

Dan Kohn


Well, community looks quite dead these days (hope I'm wrong)

The answer is:

a) yes, this is the way to deal with it

b) the way to check if you are in secure mode or not depends on the environment as well:

if process.env.NODE_ENV == 'production'
  is_secure = (req) ->
    req.headers['x-forwarded-proto'] == 'https'
else
  is_secure = (req) -> req.secure

ADD If you wish to force HTTPS:

redirect_to_https = (req, res, next) ->
  if not is_secure(req)
    res.redirect config.SECURE_DOMAIN + req.url
  else
    next()

app
  .use(redirect_to_https)
like image 20
Guard Avatar answered Oct 19 '22 23:10

Guard


You can use app.enable('trust proxy'), then req.secure boolean (http/https) works also on Heroku, or behind any compatible SSL Termination proxy.

like image 22
zakjan Avatar answered Oct 19 '22 23:10

zakjan