Automatically connect / redirect HTTPS using node.js / express

I am trying to configure HTTPS using the node.js project I'm working on. I basically completed the node.js documentation in this example:

// curl -k https://localhost:8000/ var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem') }; https.createServer(options, function (req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); 

Now when i do

 curl -k https://localhost:8000/ 

I get

 hello world 

as was expected. But if I do

 curl -k http://localhost:8000/ 

I get

 curl: (52) Empty reply from server 

In retrospect, it seems obvious that it will work this way, but at the same time, people who end up visiting my project will not type https : // yadayada, and I want all the traffic will be https from the moment they get to the site .

How can I get node (and Express, since this is the infrastructure used) to pass all incoming traffic to https, regardless of whether it was specified or not? I could not find the documentation that addressed this. Or is it supposed that there is something in front of the node environment that is in front of it (e.g. nginx) that handles this type of redirect?

This is my first foray into web development, so please forgive my ignorance if this is something obvious.

+148
express
Sep 16 '11 at 10:15
source share
16 answers

Ryan, thanks for pointing me in the right direction. I have summarized your answer (2nd paragraph) a bit with some code, and it works. In this scenario, these code fragments are placed in my express application:

 // set up plain http server var http = express.createServer(); // set up a route to redirect http to https http.get('*', function(req, res) { res.redirect('https://' + req.headers.host + req.url); // Or, if you don't want to automatically detect the domain name from the request header, you can hard code it: // res.redirect('https://example.com' + req.url); }) // have it listen on 8080 http.listen(8080); 

The https express server listens for ATM at 3000. I set these iptables rules so that node does not start as root:

 iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080 iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 3000 

All together, it works exactly the way I wanted it.

+152
Sep 17 2018-11-11T00:
source

If you follow regular ports because HTTP is trying to port 80 by default and HTTPS is trying port 443 by default, you can just have two servers on the same computer: Here is the code:

 var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('./key.pem'), cert: fs.readFileSync('./cert.pem') }; https.createServer(options, function (req, res) { res.end('secure!'); }).listen(443); // Redirect from http port 80 to https var http = require('http'); http.createServer(function (req, res) { res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url }); res.end(); }).listen(80); 

Test with https:

 $ curl https://127.0.0.1 -k secure! 

With http:

 $ curl http://127.0.0.1 -i HTTP/1.1 301 Moved Permanently Location: https://127.0.0.1/ Date: Sun, 01 Jun 2014 06:15:16 GMT Connection: keep-alive Transfer-Encoding: chunked 

Read more: Nodejs HTTP and HTTPS for the same port

+107
Jun 01 '14 at 6:18
source

Thanks to this guy: https://www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html

 app.use (function (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 res.redirect('https://' + req.headers.host + req.url); } }); 
+59
Mar 08 '18 at 15:31
source

With Nginx, you can use the header "x-forwarded-proto":

 function ensureSec(req, res, next){ if (req.headers["x-forwarded-proto"] === "https"){ return next(); } res.redirect("https://" + req.headers.host + req.url); } 
+22
May 01 '12 at 18:57
source

As of 0.4.12 we don’t have a real clean way to listen on HTTP and HTTPS on the same port using Node HTTP / HTTPS servers.

Some people solved this problem by having a Node HTTPS server (this also works with Express.js), listening on 443 (or some other port), and also has a small HTTP server attached to 80 and redirecting users to a secure port.

If you absolutely must be able to process both protocols on the same port, you need to put nginx, lighttpd, apache or some other web server on this port and act as a reverse proxy for Node.

+12
Sep 16 2018-11-11T00:
source

This answer needs to be updated to work with Express 4.0. This is how I got a separate HTTP server to work with:

 var express = require('express'); var http = require('http'); var https = require('https'); // Primary https app var app = express() var port = process.env.PORT || 3000; app.set('env', 'development'); app.set('port', port); var router = express.Router(); app.use('/', router); // ... other routes here var certOpts = { key: '/path/to/key.pem', cert: '/path/to/cert.pem' }; var server = https.createServer(certOpts, app); server.listen(port, function(){ console.log('Express server listening to port '+port); }); // Secondary http app var httpApp = express(); var httpRouter = express.Router(); httpApp.use('*', httpRouter); httpRouter.get('*', function(req, res){ var host = req.get('Host'); // replace the port in the host host = host.replace(/:\d+$/, ":"+app.get('port')); // determine the redirect destination var destination = ['https://', host, req.url].join(''); return res.redirect(destination); }); var httpServer = http.createServer(httpApp); httpServer.listen(8080); 
+8
Apr 25 '14 at 2:29
source

I find req.protocol works when I use express (not tested without, but I suspect it works). using current node 0.10.22 with expression 3.4.3

 app.use(function(req,res,next) { if (!/https/.test(req.protocol)){ res.redirect("https://" + req.headers.host + req.url); } else { return next(); } }); 
+6
Nov 22 '13 at 11:23
source

If your application is behind a trusted proxy (for example, AWB ELB or properly configured nginx), this code should work:

 app.enable('trust proxy'); app.use(function(req, res, next) { if (req.secure){ return next(); } res.redirect("https://" + req.headers.host + req.url); }); 

Notes:

  • It is assumed that you host your site on 80 and 443, if not, you will need to change the port when redirecting
  • It also assumes that you are terminating SSL on a proxy. If you use SSL from end to end, use the answer from @basarat above. An exception is the SSL connection.
  • app.enable ("trusted proxy") allows you to express X-Forwarded-Proto header verification
+5
Oct 09 '16 at 10:08
source

Most answers suggest using the req.headers.host header.

The host header requires HTTP 1.1, but it is actually optional, as the HTTP client cannot actually be sent, and node / express will accept this request.

You may ask: which HTTP client (e.g. browser) can send a request that is not in this header? The HTTP protocol is very trivial. You can process the HTTP request in several lines of code so that you do not send the host header, and if every time you receive a rejected request, you throw an exception, and depending on how you handle such exceptions, it may take your server.

So always check all the input . This is not paranoia, I received requests that do not have a host header in my service.

Also, never treat URLs as strings . Use the URL node module to modify specific parts of the string. String handling of URLs can be used in many ways. Do not do this.

+4
Jul 28 '16 at 18:05
source

You can use the express-force-https module:

npm install --save express-force-https

 var express = require('express'); var secure = require('express-force-https'); var app = express(); app.use(secure); 
+4
Feb 22 '18 at 20:52
source

you can use a "clean" module to listen for HTTP and HTTPS on the same port

 var https = require('https'); var http = require('http'); var fs = require('fs'); var net=require('net'); var handle=net.createServer().listen(8000) var options = { key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'), cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem') }; https.createServer(options, function (req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(handle); http.createServer(function(req,res){ res.writeHead(200); res.end("hello world\n"); }).listen(handle) 
+3
Nov 23 '12 at 2:37
source
 var express = require('express'); var app = express(); app.get('*',function (req, res) { res.redirect('https://<domain>' + req.url); }); app.listen(80); 

This is what we use and it works great!

+3
Nov 03 '15 at 19:04
source

I am using the solution provided by Basarat, but I also need to reload the port because I had two different ports for the HTTP and HTTPS protocols.

 res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url }); 

I also prefer to use a non-standard port to start nodejs without root privileges. I like 8080 and 8443 becaouse I come from many years of tomcat programming.

My full file will become

  var fs = require('fs'); var http = require('http'); var http_port = process.env.PORT || 8080; var app = require('express')(); // HTTPS definitions var https = require('https'); var https_port = process.env.PORT_HTTPS || 8443; var options = { key : fs.readFileSync('server.key'), cert : fs.readFileSync('server.crt') }; app.get('/', function (req, res) { res.send('Hello World!'); }); https.createServer(options, app).listen(https_port, function () { console.log('Magic happens on port ' + https_port); }); // Redirect from http port to https http.createServer(function (req, res) { res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url }); console.log("http request, will go to >> "); console.log("https://" + req.headers['host'].replace(http_port,https_port) + req.url ); res.end(); }).listen(http_port); 

Then I use iptable to format 80 and 443 traffic on my HTTP and HTTPS ports.

 sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080 sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443 
+3
Oct. 20 '17 at 15:24
source

This worked for me:

 app.use(function(req,res,next) { if(req.headers["x-forwarded-proto"] == "http") { res.redirect("https://[your url goes here]" + req.url, next); } else { return next(); } }); 
+2
Dec 10 '16 at 16:50
source

You can create 2 Node.js servers - one for HTTP and HTTPS

You can also define the configuration function that both servers will perform so you don't have to write a lot of duplicate code.

Here's how I did it: (using restify.js, but should work for express.js, or node)

http://qugstart.com/blog/node-js/node-js-restify-server-with-both-http-and-https/

+1
Feb 28 '12 at 23:01
source

This worked for me:

 /* Headers */ require('./security/Headers/HeadersOptions').Headers(app); /* Server */ app.use(favicon(__dirname + '/public/favicon.ico')); const ssl = { key: fs.readFileSync('security/ssl/cert.key'), cert: fs.readFileSync('security/ssl/cert.pem') }; //https server https.createServer(ssl, app).listen(443, '192.168.1.2' && 443, '127.0.0.1'); //http server app.listen(80, '192.168.1.2' && 80, '127.0.0.1'); app.use(function(req, res, next) { if(req.secure){ next(); }else{ res.redirect('https://' + req.headers.host + req.url); } }); 

We recommend adding headers before redirecting to https

Now that you do:

 curl http://127.0.0.1 --include 

You get:

 HTTP/1.1 302 Found // Location: https://127.0.0.1/ Vary: Accept Content-Type: text/plain; charset=utf-8 Content-Length: 40 Date: Thu, 04 Jul 2019 09:57:34 GMT Connection: keep-alive Found. Redirecting to https://127.0.0.1/ 

I am using express 4.17.1

0
Jul 04 '19 at 10:19
source



All Articles