Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Heroku NodeJS http to https ssl forced redirect

I have an application up and running on Heroku with Express.js on Node.js with https. How do I identify the protocol to force a redirect to https with Node.js on Heroku?

My app is just a simple http-server, it doesn't (yet) realize Heroku is sending it https-requests:

// Heroku provides the port they want you on in this environment variable (hint: it's not 80) app.listen(process.env.PORT || 3000); 
like image 572
Derek Bredensteiner Avatar asked Aug 25 '11 04:08

Derek Bredensteiner


People also ask

Does Heroku automatically use HTTPS?

The Heroku router doesn't have this feature. Show activity on this post. As of July 2019, Heroku supports HTTPS redirects to OTHER DOMAINS OR SUBDOMAINS (a redirect cannot point to itself). All you have to do is configure a URL record for your domain as outlined in this post.

Does Heroku work with yarn?

Heroku uses the lockfiles, either the package-lock. json or yarn.


2 Answers

As of today, 10th October 2014, using Heroku Cedar stack, and ExpressJS ~3.4.4, here is a working set of code.

The main thing to remember here is that we ARE deploying to Heroku. SSL termination happens at the load balancer, before encrypted traffic reaches your node app. It is possible to test whether https was used to make the request with req.headers['x-forwarded-proto'] === 'https'.

We don't need to concern ourselves with having local SSL certificates inside the app etc as you might if hosting in other environments. However, you should get a SSL Add-On applied via Heroku Add-ons first if using your own certificate, sub-domains etc.

Then just add the following to do the redirect from anything other than HTTPS to HTTPS. This is very close to the accepted answer above, but:

  1. Ensures you use "app.use" (for all actions, not just get)
  2. Explicitly externalises the forceSsl logic into a declared function
  3. Does not use '*' with "app.use" - this actually failed when I tested it.
  4. Here, I only want SSL in production. (Change as suits your needs)

Code:

 var express = require('express'),    env = process.env.NODE_ENV || 'development';   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.configure(function () {           if (env === 'production') {         app.use(forceSsl);     }      // other configurations etc for express go here...  }); 

Note for SailsJS (0.10.x) users. You can simply create a policy (enforceSsl.js) inside api/policies:

module.exports = function (req, res, next) {   'use strict';   if ((req.headers['x-forwarded-proto'] !== 'https') && (process.env.NODE_ENV === 'production')) {     return res.redirect([       'https://',       req.get('Host'),       req.url     ].join(''));   } else {     next();   } }; 

Then reference from config/policies.js along with any other policies, e.g:

'*': ['authenticated', 'enforceSsl']

like image 73
arcseldon Avatar answered Sep 19 '22 21:09

arcseldon


The answer is to use the header of 'x-forwarded-proto' that Heroku passes forward as it does it's proxy thingamabob. (side note: They pass several other x- variables too that may be handy, check them out).

My code:

/* At the top, with other redirect methods before other routes */ app.get('*',function(req,res,next){   if(req.headers['x-forwarded-proto']!='https')     res.redirect('https://mypreferreddomain.com'+req.url)   else     next() /* Continue to other routes if we're not redirecting */ }) 

Thanks Brandon, was just waiting for that 6 hour delay thing that wouldn't let me answer my own question.

like image 34
Derek Bredensteiner Avatar answered Sep 20 '22 21:09

Derek Bredensteiner