When you want to redirect/force an application's routes to use the HTTPS protocol, stereotypically you use the web server (NGINX, Apache, IIS, etc) to force the application to use a specific protocol and then resolve the routes that way. But what happens when you use a cloud based service like Heroku? What's more, what happens when you use NodeJS where the server's process is pointed at a specific file and not a directory; ultimately eliminating the use of the .htaccess
file?
I recently ran across such an issue when deploying an ApostropheCMS website to Heroku. Since you are reading this article I will assume that you know what Apostrophe CMS is (if not, it's a NodeJS-powered enterprise CMS with a WYSIWYG/GUI editing interface like that of Sitecore/Adobe Experience Manager). Heroku offers free SSL certificates with an application's custom domains, but how do you redirect all requests to use the HTTPS protocol?
Step 1
First you need to be on a paid Heroku dyno that starts around $7 per-month.
Step 2
We will be using the heroku-ssl-redirect
plugin. Add it to your ApostropheCMS like so:
yarn add heroku-ssl-redirect
or
npm install heroku-ssl-redirect --save
Step 3
Here is where the real magic starts to happen. If you haven't already, we are going to create a new global Apostrophe CMS module. To do so, we can use the ApostropheCMS CLI like so:
apos create-module apostrophe-global
The result will be a new module directory and accompanying nested index.js
created within the modules
directory.
Step 4
Finally we need to configure the new global module that we just created:
var sslRedirect = require('heroku-ssl-redirect');
module.exports = {
construct: function(self, options, callback) {
self.apos.app.use(sslRedirect());
callback();
}
};
So what we have done is coupled the heroku-ssl-redirect
module to the base Express process that is running behind Apostrophe. This block of code will run on every request.
In case you want to know what is going on behind this scenes, this is what the module is doing:
/**
* Force load with https on production environment
* https://devcenter.heroku.com/articles/http-routing#heroku-headers
*/
module.exports = function(environments, status) {
environments = environments || ['production'];
status = status || 302;
return function(req, res, next) {
if (environments.indexOf(process.env.NODE_ENV) >= 0) {
if (req.headers['x-forwarded-proto'] != 'https') {
res.redirect(status, 'https://' + req.hostname + req.originalUrl);
}
else {
next();
}
}
else {
next();
}
};
};
As you can see, what we have done is basically attached a custom middleware to each request within Apostrophe; forcing every request to use the HTTPS protocol.
As I had issues trying to figure this out for myself, hopefully this will demystify ApostropheCMS a little more for you. Cheers.