Is there any way to configure a node js application with express js 4 to serve some pages under http protocol and other, those which need more security, in https?
I describe my problem: I'm developing a shop online and I want to display certain pages, like the products list or the product detail views under http
, and others which I think need more security, like login or the shopping cart views, under https
protocol.
I have tried the express-force-ssl
module, but it isn't working. The following code snippet is not from my app (which is too dirty) it is just an example which alos doesn't work for me:
var express = require('express');
var forceSSL = require('express-force-ssl');
var fs = require('fs');
var http = require('http');
var https = require('https');
var ssl_options = {
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-certificate.pem'),
ca: fs.readFileSync('./server-certificate-signing-request.pem')
};
var app = express();
var server = http.createServer(app);
var secureServer = https.createServer(ssl_options, app);
app.use(forceSSL);
app.get('/', function (req, res, next) {
res.send('<a href="/user/userA">Hello</a>')
});
app.get('/user/:name', function (req, res, next) {
var user = req.params.name;
res.send('<a href="/login">Hello ' + user + '</a>')
});
app.get('/login', forceSSL, function (req, res, next) {
res.send('<a href="/">Hello</a><br/><a href="/logout">Goodbye</a>')
});
app.get('/logout', forceSSL, function (req, res, next) {
res.send('<a href="/">Hello</a>')
});
secureServer.listen(443)
server.listen(8085)
console.log('server started');
The result is that when I launch the application, with url http://localhost:8085
, the server automatically redirects it to https://localhost
and serves all pages in https
protocol.
What I want is to start on http://localhost:8085
, navigate to http://localhost/user/userA
, then from it go to https://localhost/login
and, if click on "Hello" link, I would like to be redirected to http://localhost:8085
.
Is there any missing code to get the behavior I want or even any other way to reach it without express-force-ssl
module?
I have asked to the author of express-force-ssl
module and he has told me that the redirect behavior works as expected. Here is the post.
But diving a little more in its code I've created a custom plugin to solve my problem. Here is the code:
var parseUrl = require('url').parse;
var isSecure = function (req) {
if (req.secure) {
return true;
}
else if (
req.get('X-Forwarded-Proto') &&
req.get('X-Forwarded-Proto').toLowerCase &&
req.get('X-Forwarded-Proto').toLowerCase() === 'https') {
return true;
}
return false;
};
exports = module.exports = function (req, res, next) {
if (isSecure(req)) {
if (req.method === "GET") {
var httpPort = req.app.get('httpPort') || 80;
var fullUrl = parseUrl(req.protocol + '://' + req.header('Host') + req.originalUrl);
res.redirect('http://' + fullUrl.hostname + ':' + httpPort + req.originalUrl);
}
else {
next();
}
}
else {
next();
}
};
It's very similar to force-ssl
file but here we manage the opposite action, i.e., here I redirect to http
when a route is forced to it. So it's needed to add the function to every route we want to see under http
protocol:
var express = require('express');
var forceSSL = require('express-force-ssl');
var fs = require('fs');
var http = require('http');
var https = require('https');
var useHttp = require('./useHttp');
var ssl_options = {
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-certificate.pem')
};
var app = express();
var server = http.createServer(app);
var secureServer = https.createServer(ssl_options, app);
app.get('/', useHttp, function (req, res, next) {
res.send('<a href="/user/userA">Hello</a>')
});
app.get('/user/:name', useHttp, function (req, res, next) {
var user = req.params.name;
res.send('<a href="/login">Hello ' + user + '</a>')
});
app.get('/login', forceSSL, function (req, res, next) {
res.send('<a href="/">Hello</a><br/><a href="/logout">Goodbye</a>')
});
app.get('/logout', forceSSL, function (req, res, next) {
res.send('<a href="/">Hello</a>')
});
app.set('httpsPort', 9090);
app.set('httpPort', 8085);
secureServer.listen(9090)
server.listen(8085)
console.log('server started');
As you can see I need now to specify in all routes which protocol use: useHttp
for http
or forceSSL
for https
.
Although I'm not comfortable at all with this solution because I have to specify in all routes which kind of protocol I want. But at least it works. So I would be very pleased if someone finds any other solution, for isntance, adding in middleware layer a function to manage all http
requests and just redirect to https
when it is specified with forceSSL
. By the moment, this works.