Express-Sessions with NGINX Loadbalancer Using SSL Termination

How do I make session data persist with Express-Session when using NGINX Loadbalancer performing SSL termination?


I have recently switched from a single node application that loaded SSL certs and managed sessions to having NGINX handle load distribution and SSL termination for a cluster of nodes.

The issue I am having is when I successfully verify the user and set req.session.user = user it doesn't stick after res.redirect(backURL).

Primary Express.js application code:

File: server.js

var app = require('./app.js');
var http = require('http');

app.set('port', process.env.PORT || 80);

var httpServer = http.createServer(app).listen(app.get('port'));

File: app.js

module.exports = function () {      
    var express      = require('express');
    var uuid         = require('node-uuid');
    var session      = require('express-session');
    var path         = require('path');
    var cookieParser = require('cookie-parser');
    var bodyParser   = require('body-parser');

    var app = express();

    var route    = require('./routes/index');

    // view engine setup
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'jade');

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(cookieParser());
    app.use(express.static(path.join(__dirname, 'public')));

    //For session handling.
    app.use(session({ 
      genid: function() {
          return uuid.v4();
      },
      key:    'connect.sid',
      secret: 'secret key',
      cookie: { 
              expires: new Date(Date.now() + hour),
              maxAge:  hour 
      },
      saveUninitialized: true,
      resave: true
    }));
    app.use(function(req, res, next){
      if(!req.session.user){
        var sess = req.session;
        sess.user = {credentials: {alias: 'Guest'}};
      }
      return next();
    });

    app.use('/', route);

    return app;
};

NGINX config:

upstream d1 {
    ip_hash;
    server ip-***.ec2.internal;
}

server {
    listen 80;
    server_name d1.com;
    return 301 https://$server_name$request_uri;
}

server {
       listen 443 ssl;
       server_name d1.com;

       ssl on;
       ssl_certificate         /etc/nginx/ssl/d1.com/server.crt;
       ssl_certificate_key     /etc/nginx/ssl/d1.com/server.key;

       ssl_session_timeout 5m;
       ssl_session_cache shared:SSL:50m;

       ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
       ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:ECDHE-RSA-AES128$
       ssl_prefer_server_ciphers on;

       add_header Strict-Transport-Security max-age=15768000;

       ssl_stapling on;
       ssl_stapling_verify on;

       location / {
           proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header X-Forwarded-Proto $scheme;
           proxy_set_header X-NginX-Proxy true;

           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";

           proxy_pass http://d1;
           proxy_redirect off;
       }

       location ~* \.(css|js|gif|jpe?g|png)$ {
           expires 168h;
           proxy_pass http://d1;    
       }
}