How to force SSL / https in Express.js

I am trying to create middleware for Express.js to redirect all unsafe traffic (port 80) to a secure SSL port (443). Unfortunately, there is no information in the Express.js request that allows us to determine if the request goes through http or https.

One solution would be to redirect the request each , but this is not an option for me.

Notes:

  • There is no way to handle Apache or anything else. It has in node.

  • Only one server can be started in the application.

How would you solve this?

+43
redirect ssl express
Dec 22 '11 at 15:03
source share
8 answers

Since I was working on nginx, I had access to the x-forwarded-proto header property, so I could write a little middleware to redirect all traffic, as described here: http://elias.kg/post/14971446990/force- ssl-with-express-js-on-heroku-nginx

Edit: URL updated

0
Dec 28 '11 at 11:41
source

Although the question looks a year ago, I would like to answer, as this may help others. Its really simple with the latest version of expressjs (2.x). First create a key and certificate using this code

openssl genrsa -out ssl-key.pem 1024

$ openssl req -new -key ssl-key.pem -out certrequest.csr .. $ openssl req -new -key ssl-key.pem -out certrequest.csr

$ openssl x509 -req -in certrequest.csr -signkey ssl-key.pem -out ssl-cert.pem

Store the certificate and key files in the folder containing app.js. Then edit the app.js file and write the following code before express.createServer ()

 var https = require('https'); var fs = require('fs'); var sslkey = fs.readFileSync('ssl-key.pem'); var sslcert = fs.readFileSync('ssl-cert.pem') var options = { key: sslkey, cert: sslcert }; 

Now pass the options object to the createServer () function

 express.createServer(options); 

Done!

+22
Jun 14 2018-12-12T00:
source

First, let me see if I can clarify the problem. You are limited to one (1) node.js process, but this process can listen on two (2) network ports, both 80 and 443, right? (When you say one server, it is not cleared if you mean one process or only one network port).

Given this limitation, it seems that the problem is for reasons that you do not provide, somehow your clients are connecting to the wrong port. This is a bizarre extreme case, because by default clients will send HTTP requests to port 80 and HTTPS to port 443. And when I say "default", I mean if certain ports are not specified in the URLs. Therefore, unless you explicitly use cross-URLs such as http: //example.com-00-0043 and https://example.com:80 , you really shouldn't have cross-traffic coming to your site. But since you asked the question, I think you should have it, although I'm sure you are using non-standard ports, not the default 80/443.

So, for the background: YES, some web servers handle this pretty well. For example, if you run http://example.com-00-0043 on nginx, it will respond to an HTTP 400 "Bad Request" response indicating "A simple HTTP request has been sent to the HTTPS port." YES, you can listen to both 80 and 443 from the same node.js. process. You just need to create 2 separate instances of express.createServer() so that there are no problems. Here's a simple program to demonstrate the processing of both protocols.

 var fs = require("fs"); var express = require("express"); var http = express.createServer(); var httpsOptions = { key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem') }; var https = express.createServer(httpsOptions); http.all('*', function(req, res) { console.log("HTTP: " + req.url); return res.redirect("https://" + req.headers["host"] + req.url); }); http.error(function(error, req, res, next) { return console.log("HTTP error " + error + ", " + req.url); }); https.error(function(error, req, res, next) { return console.log("HTTPS error " + error + ", " + req.url); }); https.all('*', function(req, res) { console.log("HTTPS: " + req.url); return res.send("Hello, World!"); }); http.listen(80); 

And I can verify this via cURL as follows:

 $ curl --include --silent http://localhost/foo HTTP/1.1 302 Moved Temporarily X-Powered-By: Express Content-Type: text/html Location: https://localhost/foo Connection: keep-alive Transfer-Encoding: chunked <p>Moved Temporarily. Redirecting to <a href="https://localhost/foo">https://localhost/foo</a></p> $ curl --include --silent --insecure https://localhost:443/foo HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 13 Connection: keep-alive Hello, World!% 

And showing a redirect from HTTP to HTTPS ...

 curl --include --silent --location --insecure 'http://localhost/foo?bar=bux' HTTP/1.1 302 Moved Temporarily X-Powered-By: Express Content-Type: text/html Location: https://localhost/foo?bar=bux Connection: keep-alive Transfer-Encoding: chunked HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 13 Connection: keep-alive Hello, World!% 

So, this will work to serve both protocols for the normal case and redirect correctly. However, crosses do not work at all. I believe that a request with cross-references to the express server will not be redirected through the middleware stack, since it will be considered as an error from the very beginning and will not even be able to correctly process the URI request, which is necessary for sending it through the middleware chain route. The express stack did not even receive them, I think, because they are not valid requests, therefore they are ignored somewhere in the node TCP stack. You can probably write a server to do this, and there may already be a module there, but you will have to write it right at the TCP level. And you will have to detect a normal HTTP request in the first piece of client data that gets to the TCP port and connect this connection to the HTTP server instead of the usual TLS acknowledgment.

When I do this, my explicit error handlers do NOT receive the call.

 curl --insecure https://localhost:80/foo curl: (35) Unknown SSL protocol error in connection to localhost:80 curl http://localhost:443/foo curl: (52) Empty reply from server 
+21
Dec 26 '11 at 8:19
source

Just in case, if you host on Heroku and just want to redirect to HTTPS regardless of the port, here we use the intermediate solution that we use.

No need to redirect if you are developing locally.

 function requireHTTPS(req, res, next) { // The 'x-forwarded-proto' check is for Heroku if (!req.secure && req.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV !== "development") { return res.redirect('https://' + req.get('host') + req.url); } next(); } 

You can use it with Express (2.x and 4.x) as follows:

 app.use(requireHTTPS); 
+15
Jun 30 '15 at 17:44
source

Based on Elias answer, but with embedded code. This works if you have a node behind nginx or a load balancer. Nginx or the balancer will always push node to plain old http, but it sets the header so you can distinguish.

 app.use(function(req, res, next) { var schema = req.headers['x-forwarded-proto']; if (schema === 'https') { // Already https; don't do anything special. next(); } else { // Redirect to https. res.redirect('https://' + req.headers.host + req.url); } }); 
+10
Dec 11 '13 at 22:43
source

http.createServer (app.handle) .listen (80)

https.createServer (parameters, app.handle) .listen (443)

for expression 2x

+3
Dec 26 2018-11-12T00:
source

This code looks like it does what you need: https://gist.github.com/903596

+2
Mar 14 2018-12-12T00:
source

Try this example:

 var express = require('express'); var app = express(); // set up a route to redirect http to https app.use(function (req, res, next) { if (!/https/.test(req.protocol)) { res.redirect("https://" + req.headers.host + req.url); } else { return next(); } }); var webServer = app.listen(port, function () { console.log('Listening on port %d', webServer.address().port); }); 
+2
Jan 17 '17 at 10:36
source



All Articles