NodeJS behind AWS ELB redirecting http to https failed health check - node.js

I configured the ELB to open up both 80 and 443, where 443 is configured with SSL. Both ELB ports points to the instances' 80 port. The ELB's heatlh check is using ping target HTTP:80/index.html. It used to work, until I recently decided to start redirecting http to https.
Now the following codes are on the server.js(the codes within // are my newly added codes):
//
app.use(function(req, res, next) {
if (config.env === 'prod' && req.get('X-Forwarded-Proto') !== 'https') {
console.log("redirecting")
console.log('https://' + req.get('Host') + req.url)
res.set('X-Forwarded-Proto', 'https');
res.redirect('https://' + req.get('Host') + req.url);
}
else
next();
});
//
app.use(express.static(path.join(__dirname, 'home')));
app.set('trust proxy'); // enable this to trust the proxy
app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
app.get("/*", function(req, res, next) {
res.sendFile(path.join(__dirname, 'home/index.html'));
});
I supposed the above request will redirect all request to the elb server but with https protocol.
However the server start printing:
redirecting
https://10.x.x.xx/index.html
And then the ELB is failing as https://10.x.x.xx/inde.html is not available.
However the index.html's location right under the {domain}/.
I think the way I redirect may be wrong - but I have no idea how to fix it.

My solution was to check for the health API route prior to redirecting.
Set up Health check on AWS load balancer to /api/health
Add condition
if (config.env === 'prod' && req.get('X-Forwarded-Proto') !== 'https' && req.url !== '/api/health') {
// redirect
}

The load balancer health check requests won't have the x-forwarded-proto header, since they haven't been forwarded from a client, they are coming directly from the load balancer. You probably need to check if the header exists first, before doing the redirect.
Also I'm a bit confused about your SSL setup. Are you doing SSL termination at the Load Balancer, but also serving an SSL certificate at the EC2 server? You say "Both ELB ports points to the instances' 80 port", which means you aren't serving an SSL certificate on the EC2 server, so your health check URL should be http instead of https.
Also this line that you have right before the redirect accomplishes nothing at all:
res.set('X-Forwarded-Proto', 'https');

Related

How do you write an SSL redirect in NodeJS running on AWS EC2 without using port 80 (http) or port 43 (https)?

I have two node servers on a single host. One HTTP server with the responsibility of redirecting to HTTPS, and one HTTPS server responsible for serving my application:
const express = require('express');
const https = require('https');
const http = require('http')
const fs = require('fs');
const app = express();
const httpsOptions = {
key: fs.readFileSync('./local-ssl/key.pem'),
cert: fs.readFileSync('./local-ssl/cert.pem'),
passphrase: '*****'
}
//other stuff
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
res.end();
}).listen(80);
https.createServer(httpsOptions, app).listen(443)
This works great locally.
The issue is, when I deploy this code and run these servers on AWS EC2 you cannot start servers on ports 80 and 443. I am trying to figure out how I can get around this issue. If I run them on different ports, the servers will not respond, and worse, redirect incorrectly.
Example:
If I serve HTTP on 8081 and HTTPS on 8443, when a redirect occurs, the code redirects to
https://my-fun-url.com:8081
which of course does not work because I am not responding to HTTPS on port 8081.
Now, I've explored the option of port forwarding, but how would this work? If I forward ports 80 and 443 to internal ports (let's say) 3000 and 4000 the same redirection problem will occur.
I have scoured the internet for so long and to me this is a simple requirement for any web-app. I would very much appreciate some detailed guidance on my strategy.
If you want to keep ports 8081 and 8443, then you simply replace 8081 with 8443 in the host header:
httpsHost = req.headers.host.replace('8081', '8443');
res.writeHead(301, {
"Location": "https://" + httpsHost + req.url
});
Now, I've explored the option of port forwarding, but how would this work? If I forward ports 80 and 443 to internal ports (let's say) 3000 and 4000 the same redirection problem will occur.
Not exactly. When someone navigates to http://my-fun-url.com (80) the request is forwarded to 3000. Your http server will respond with a redirect to https://my-fun-url.com (443) which will be forwarded to 4000, and the https server will take it from there.
The difference between the two methods is that with ports 80 and 443 being the default, they are implied and therefore can be left out from the host part of the URL. Which makes the redirect easier as there's no port in the host to replace in the first place, just the protocol part (HTTP/HTTPS).

Why do I have to type the ":80" in https://localhost:80 for my website to load?

I recently secured my website on node.js to use https instead of just plain http. However, once I did this, I realized that I had to type out the :80 suffix if I wanted to get to my website to load. Why is this? Doesn't chrome default to port 80 and shouldn't https://localhost suffice?
const port = 80;
https.createServer({
key: fs.readFileSync('./private/ssl/server.key'),
cert: fs.readFileSync('./private/ssl/server.cert')
}, app)
.listen(port, function () {
console.log('Server running on port: ' + port);
});
app.get('/', (req, res) => {
res.sendFile('index.html', { root: path.join(__dirname, './') });
});
app.use(express.static('./public'));```
The default port for HTTPS is 443, not 80.
Be aware that HTTPS uses port 443 by default, and that's probably the source of your confusion.
If you specify both https and :80 in your browser's address bar, you are making an HTTPS request to port 80, which is unusual.
What kind of reply you will be getting depends on your server's configuration.

Redirect from http to https in Node.js and Express.js using Windows

I've seen this topic about the same issue but the provided solutions don't seem to change the port from 3000 to the default 443 used with SSL.
I've seen in some places iptables should be used to accomplish it, but in a windows environment this can not be done that way.
Temporarily I've fixed it by manually removing the 3000 port by myself:
//redirecting to HTTPS if the connection is not secure
app.use(function(req,resp,next){
if (!req.secure) {
return resp.redirect(301, 'https://' + req.headers.host.replace(':3000', '') + req.url);
} else {
return next();
}
});
Any proper solution for it?

Having express.js or node accept an http connection over port 443

I have a REST API built with node that communicates over SSL. The server is built uses express and makes use of vhosts and cors. I have recently added a listener on port 80 as well so I can force HTTPS. As I test, I tried to access http://manage.domain.com:443/ but the request just hangs. Neither listeners seem to accept it. All I want to do is redirect that request to https.
I assume you already know this, but you'll need an https server (duh) to serve the HTTPS content. It doesn't matter what port you run it on; 443 is just the default port for HTTPS. If you want HTTP requests to redirect to HTTPS, you'll need both an http and an https server. Here's an example of how your app file should look:
var http = require('http'),
https = require('https'),
express = require('express')
fs = require('fs');
var domain = 'localhost';
var app = express();
app.get('*', function(req, res){
// redirect to HTTPS
res.redirect('https://' + domain + req.path);
});
http.createServer(app).listen(80, function(){
console.log('HTTP listening on port 80');
});
var appSecure = express();
// configure your app here
var options = {
key: fs.readFileSync('ssl_key.pem'),
cert: fs.readFileSync('ssl_cert.crt'),
};
https.createServer(options, appSecure).listen(443, function(){
console.log('HTTPS listening on port 443');
});
Obviously, you will need your SSL key and certificate to make this work.
As you probably know, most systems require elevated privileges to open a port less than 1025; so if you use port 80 and port 443, you'll have to run the app server with elevated privileges (if you're running on OSX/Linux/BSD, just do sudo node app.js).

HTTPS on Nodejitsu using Express

Okay. I have an app on express which also uses Socket.io and works fine via HTTP. However, now I have to move to HTTPS. Nodejitsu provide a lot of documentation on this. They suggest to use node-http-proxy (https://github.com/nodejitsu/node-http-proxy). Fine!
From the code for HTTP:
var server = http.createServer(app) // app is an Express instance
server.listen(config.port,config.hostip) // config.port is 80 for localhost and 3000 for Nodejitsu, config.hostip is 127.0.0.1 for localhost and 0.0.0.0 for Nodejitsu
I got this:
var server = http.createServer(app)
var options = {
https: {
key: fs.readFileSync(__dirname+"/ssl/privatekey.pem", 'utf8'),
cert: fs.readFileSync(__dirname+"/ssl/certificate.pem", 'utf8')
}
}
httpProxy.createServer(config.port, config.hostip, options).listen(3001,config.hostip)
var proxy = new httpProxy.HttpProxy({
target: {
host: config.hostip,
port: config.port
}
})
https.createServer(options.https, function (req, res) {
proxy.proxyRequest(req, res)
}).listen(3002,config.hostip)
server.listen(config.port,config.hostip)
When I finally deploy (no errors during deployment), I visit the page and see 502 error Socket hang up. OK, I might doing something wrong, so I just copy and paste the example from https://github.com/nodejitsu/node-http-proxy "Proxying to HTTP from HTTPS" to check if it works. But it doesn't - 502 error.
It works fine on my localhost though. I have also tried to launch standalone HTTPS server without node-https-proxy, but no luck. Please help, I cannot solve this for weeks.
Found by myself. Nodejitsu offers SSL by default, just visit your site via HTTPS://. For custom domains to apply SSL certs you need to subscribe for Business Plan.

Resources