So the stack is this: Express & Angular deployed to Heroku. I'm trying to serve the app forced over HTTPS by using a simple redirect using the code highlighted below. However when I open up the URL in the browser, I get a 'too many redirects' error.
When I type http://[myurl].com, I can see the URL in the browser changing to https://[myurl].com but nothing shows up at that address. It just shows 'Too many redirects'. This proves that it successfully redirects but I feel like Angular is messing it up.
When I remove the redirecting code below and access HTTPS or the HTTP address manually in the browser, it works fine.
//HTTPS redirect middleware
function ensureSecure(req, res, next) {
console.log(req.protocol + process.env.PORT + '' + req.hostname + req.url);
if(req.secure || req.hostname=='localhost'){
//Secure request, continue to next middleware
next();
}else{
res.redirect('https://' + req.hostname + req.url);
console.log(req.protocol + process.env.PORT + '' + req.hostname + req.url);
}
}
//Parse the body of the request as a JSON object, part of the middleware stack (https://www.npmjs.com/package/body-parser#bodyparserjsonoptions)
app.use(bodyParser.json());
//Serve static Angular JS assets from distribution, part of the middleware stack, but only through HTTPS
app.all('*', ensureSecure);
app.use('/', express.static('dist'));
//Import routes
app.use('/api', [router_getToken, router_invokeBhApi]);
//Setup port for access
app.listen(process.env.PORT || 3000, function () {
console.log(`The server is running on port ${process.env.PORT || 3000}!`);
});
This is a sample of the heroku logs when you visit http://[myurl].com (I masked the URL):
2016-11-29T21:50:34.363391+00:00 app[web.1]: 0|app | http37436[something].com/
2016-11-29T21:50:34.363468+00:00 app[web.1]: 0|app | http37436[something].com/
2016-11-29T21:50:34.402022+00:00 app[web.1]: 0|app | http37436[something].com/
2016-11-29T21:50:34.402091+00:00 app[web.1]: 0|app | http37436[something].com/
2016-11-29T21:50:34.436006+00:00 app[web.1]: 0|app | http37436[something].com/
2016-11-29T21:50:34.437454+00:00 app[web.1]: 0|app | http37436[something].com/
2016-11-29T21:50:34.479580+00:00 app[web.1]: 0|app | http37436[something].com/
The browser (Chrome latest) shows these requests in the Network tab over & over & over again:
'Request URL:https://[myurl].com/
Request Method:GET
Status Code:302 Found'
Notice how Heroku (the console.log in the express.js code) shows that I'm making HTTP requests but my browser is saying I'm making HTTPS requests. So confused!
EDIT: I tried this as well
//HTTPS redirect middleware
function ensureSecure(req, res, next) {
console.log(req.protocol + process.env.PORT + '' + req.hostname + req.url);
if (req.secure || req.hostname == 'localhost') {
//Serve Angular App
express.static('dist');
} else {
//res.redirect('https://' + req.hostname + ':' + process.env.PORT + req.url);
res.redirect('https://[myurl].com/');
}
}
//Parse the body of the request as a JSON object, part of the middleware stack (https://www.npmjs.com/package/body-parser#bodyparserjsonoptions)
app.use(bodyParser.json());
//Serve static Angular JS assets from distribution, part of the middleware stack, but only through HTTPS
app.use('/', ensureSecure);
//Import routes
app.use('/api', [router_getToken, router_invokeBhApi]);
//Setup port for access
app.listen(process.env.PORT || 3000, function () {
console.log(`The server is running on port ${process.env.PORT || 3000}!`);
});
Found the solution!
Context: Heroku stores the origin protocol in a header variable called 'X-Forwarded-Proto'. HTTP Routing in Heroku You need to check this variable and not the protocol variable tied to the 'req' object in Express. (that is, don't check req.protocol, check req.get('X-Forwarded-Proto') instead)
Code:
//HTTPS redirect middleware
function ensureSecure(req, res, next) {
//Heroku stores the origin protocol in a header variable. The app itself is isolated within the dyno and all request objects have an HTTP protocol.
if (req.get('X-Forwarded-Proto')=='https' || req.hostname == 'localhost') {
//Serve Angular App by passing control to the next middleware
next();
} else if(req.get('X-Forwarded-Proto')!='https' && req.get('X-Forwarded-Port')!='443'){
//Redirect if not HTTP with original request URL
res.redirect('https://' + req.hostname + req.url);
}
}
You can pass the option
{ trustProtoHeader: true }
to the HTTPS() method; for example:
const enforce = require('express-sslify');
app.use(enforce.HTTPS({ trustProtoHeader: true }));
That fixed it for me
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With