Forwarding http to https in node.js express app using EBS & ELB environment - node.js

I am using the following to redirect all http requests to https requests.
I can see from logs that the header 'x-forwarded-proto' is never populated and is undefined.
app.get('*', function(req, res, next) {
//http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-proto
if (req.headers['x-forwarded-proto'] != "https") {
res.redirect('https://' + req.get('host') + req.url);
} else {
next();
}
});
It is causing a redirect loop. How can I redirect properly without looping?

edit:
my original answer below is for express 3.x, for 4.x you can get a string http or https in req.protocol, thx #BrandonClark
use req.get, not req.headers. Note that POST requests and all other non-GET will not see this middleware.
It's also possible that Express does not carry the x-forwarded-proto header across when you redirect. You may need to set it yourself.
app.get('*', function(req, res, next) {
//http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-proto
if (req.get('x-forwarded-proto') != "https") {
res.set('x-forwarded-proto', 'https');
res.redirect('https://' + req.get('host') + req.url);
} else {
next();
}
});
Another way to force https:
function ensureSecure(req, res, next){
if(req.secure){
// OK, continue
return next();
};
res.redirect('https://'+req.host+req.url); // handle port numbers if non 443
};
app.all('*', ensureSecure);

You can edit the nginx config file in the EC2 instance. SSH to ec2 instance and follow the following steps
go to /etc/nginx/conf.d
open 00_elastic_beanstalk_proxy.conf
sudo vi 00_elastic_beanstalk_proxy.conf
put
location / {
if ($http_x_forwarded_proto != 'https') {
rewrite ^ https://$host$request_uri? permanent;
}
…
}
reload nginx
sudo /usr/sbin/nginx -s reload

Related

Why does HTTP -> HTTPS redirect fail in nodejs when no subdomain is specified?

I'm running a nodejs webapp on Heroku and I want to redirect all users that access via http to https with the corresponding URL.
I have it mostly working however the user is redirected to the home page if no subdomain is specified. Any idea what is going on here?
The node rerouting middleware:
app.enable('trust proxy');
app.use((req, res, next) => {
if (req.get('X-Forwarded-Proto') !== 'https') {
res.redirect(`https://${req.headers.host + req.url}`);
} else {
next();
}
});
Works:
http://www.example.com/page redirects to https://www.example.com/page
Fails:
http://example.com/page redirects to https://www.example.com
Because req.url is an inherited property from Node.JS's http module and does not necessarily contain the original URL. Express.JS documentation also states it, https://expressjs.com/en/api.html#req.originalUrl
If you want to retain the original url you should use the correct property, which is originalUrl.
app.enable('trust proxy');
app.use((req, res, next) => {
if (req.get('X-Forwarded-Proto') !== 'https') {
res.redirect(`https://${req.headers.host + req.originalUrl}`);
} else {
next();
}
});

Redirect all URLs to secured URLs without www in Node.js

I am using the code below to redirect all my traffic to https version without www i.e. for one my application hosted on a subdomain. It should work for the following cases:
http://subdomain.domain.com
http://www.subdomain.domain.com
https://www.subdomain.domain.com
www.subdomain.domain.com
All the above should redirect to https://subdomain.domain.com.
I am trying out this on my Node.js application.
app.use('*', function(req, res, next) {
// https
if (req.headers["x-forwarded-proto"] == "https") {
// https with www
if (req.headers.host.match(/^www/) !== null) {
res.redirect(301, 'https://' + req.headers.host.replace(/^www\./, '') + req.url);
}
// https without www
else {
next();
}
}
// http
else {
// http with www
if (req.headers.host.match(/^www/) !== null) {
res.redirect(301, 'https://' + req.headers.host.replace(/^www\./, '') + req.url);
}
// http without www
else {
res.redirect("https://subdomain.domain.com" + req.url);
}
}
});
I am not able to get the redirection working. For the fourth URL i.e. www.subdomain.domain.com, I have updated my DNS as well.
Below Code Snippet Check if URL is not secure it redirects to https with replacing www from url.
app.use('*',function(req, res, next){
if (!req.secure) {
res.redirect('https://' + req.headers.host.replace(/\/\/www\./, '') + req.url);
}
next();
})

Forward HTTP to HTTPS - AWS Windows Node.js

I have a applications running on AWS Windows and Node.js. I can access using http and https. But i need it to forward http to https if anyone access through http.
I can think of many way, but would appreciate any advice on the best approach. The server is a EC2 instance, accessed through a load balancer.
If you're using express, this middleware module makes it easy to enforce https: https://www.npmjs.com/package/express-force-ssl
If you're using a reverse proxy in front of your app (ELB, nginx, etc), you'll need to set the trust proxy setting.
Here's a sample without the above module:
// Forward all requests to HTTPS.
// enable reverse proxy support in Express. This causes the
// the "X-Forwarded-Proto" header field to be trusted so its
// value can be used to determine the protocol. See
// http://expressjs.com/api#app-settings for more details.
app.enable('trust proxy');
// Add a handler to inspect the req.secure flag (see
// http://expressjs.com/api#req.secure). This allows us
// to know whether the request was via http or https.
app.use((req, res, next) => {
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
console.log('Redirecting to https');
res.redirect('https://' + req.headers.host + req.url);
}
});
Complete sample app.js
var express = require('express');
var app = express();
// Forward all requests to HTTPS.
// enable reverse proxy support in Express. This causes the
// the "X-Forwarded-Proto" header field to be trusted so its
// value can be used to determine the protocol. See
// http://expressjs.com/api#app-settings for more details.
app.enable('trust proxy');
// Add a handler to inspect the req.secure flag (see
// http://expressjs.com/api#req.secure). This allows us
// to know whether the request was via http or https.
app.use((req, res, next) => {
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
console.log('Redirecting to https');
res.redirect('https://' + req.headers.host + req.url);
}
});
// Respond to any GET requests with our message
app.get('*', (req, res) => {
res.send('This is only served over https');
});
// Listen on the assigned port
var port = process.env.PORT || 3001;
app.listen(port);
console.log('Hello started on port ' + port);
Redirect only GET requests, respond with error for non-GET requests
app.all('*', (req, res, next) => {
if (req.secure) {
next();
} else if (req.method === 'GET') {
res.redirect(`https://${req.headers.host}${req.url}`);
} else {
res.status(401).send('Secure channel required');
}
});

Redirecting HTTP requests to HTTPS not always working

I have an application written using Node. Any requests coming over HTTP is redirected to HTTPS. In some cases the redirection happens successfully (browser receives a HTTP 302) however most of the cases the redirection does not occur(browser receives just a HTTP 200).
What should i do to ensure the redirection always occurs ?
Logic i currently use
module.exports = function(req, res, next) {
console.log("Entering secure redirect route");
if(!req.headers['x-forwarded-proto']) {
console.log("Attempting to access using http protocol");
} else {
console.log("Attempting to access using https protocol");
}
console.log("Attempting to access host " + req.host);
console.log("Attempting to access url " + req.originalUrl);
if(!req.headers['x-forwarded-proto']) {
console.log("Redirecting to " + "https://" + req.host + req.url);
res.redirect("https://" + req.host + req.url);
res.end();
} else {
next();
}
} else {
next();
}
};
It's not enough that the x-forwarded-proto header is set if the value is not HTTPS. Whoever is forwarding the traffic must be accepting HTTP connections.
console.log out the value of x-forwarded-proto every time, and I suspect sometimes the value is HTTP

Amazon Elastic load balancer is not populating x-forwarded-proto header

I am trying to force all http requests to https requests and I am facing the problem as elastic load balancer not populating x-forwarded-proto header in the request.
This is the code I am using and it is causing redirect loop because of this. How would I fix this problem?
app.use (function (req, res, next) {
console.log('Request headers = ' + JSON.stringify(req.headers));
console.log('Request protocol = ' + JSON.stringify(req.protocol));
var schema = (req.headers['x-forwarded-proto'] || '').toLowerCase();
if (schema === 'https') {
next();
} else {
res.redirect('https://' + req.headers.host + req.url);
}
});
It sounds like your ELB listeners might be configured for TCP instead of HTTP. Configured for TCP, it will not add X-Forwarded-Proto or X-Forwarded-For.
Send http and https requests to two different ports. If the request comes through on the http port, you would be safe to redirect it.

Resources