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

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?

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.

NodeJS behind AWS ELB redirecting http to https failed health check

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');

HTTP redirect to HTTPS in a different port

I am trying to redirect every http request to an https server. This works fine if the port of the https server is the 443. However if I try to redirect to a different port it doesn't happen. Here is my code:
http.createServer(function(req,res){
var host = req.headers['host'],
url = req.url;
res.writeHead(301,
{"Location": "https://" + host + ":"+SERVER.SPORT + url})
res.end()
}).listen(SERVER.PORT)
https.createServer(SERVER.HTTPS_OPTIONS,app).listen(SERVER.SPORT)
Your host most likely already includes the port number.
You can make sure if you change this:
var host = req.headers['host']
to:
var host = req.headers['host'].split(':')[0];
Also add some logging:
console.log("https://" + host + ":"+SERVER.SPORT + url);
to see what the URL that you're building looks like.
You can also want to use the url module to work with URLs instead of manually concatenating strings to avoid mistakes like that. See:
https://nodejs.org/api/url.html
In any case add some logging to know what the URL that you're building looks like. Also test it with curl:
curl -v http://localhost:1234
to see what headers are returned.

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