Nodejs HTTP and HTTPS over same port
You don't need to listen on same port if you follow convention
By convention when you request http://127.0.0.1
your browser will try to connect to port 80. If you try to open https://127.0.0.1
your browser will try to connect to port 443. So to secure all traffic it is simply conventional to listen to port 80 on http with a redirect to https where we already have a listener for https for port 443. Here's 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 httpsvar 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 -ksecure!
With http:
$ curl http://127.0.0.1 -iHTTP/1.1 301 Moved PermanentlyLocation: https://127.0.0.1/Date: Sun, 01 Jun 2014 06:15:16 GMTConnection: keep-aliveTransfer-Encoding: chunked
If you must listen on same port
There isn't simple way to have http / https listen on the same port. You best bet is to create proxy server on a simple net socket that pipes to (http or https) based on the nature of the incoming connection (http vs. https).
Here is the complete code (based on https://gist.github.com/bnoordhuis/4740141) that does exactly that. It listens on localhost:3000 and pipes it to http (which in turn redirects it to https) or if the incomming connection is in https it just passes it to https handler
var fs = require('fs');var net = require('net');var http = require('http');var https = require('https');var baseAddress = 3000;var redirectAddress = 3001;var httpsAddress = 3002;var httpsOptions = { key: fs.readFileSync('./key.pem'), cert: fs.readFileSync('./cert.pem')};net.createServer(tcpConnection).listen(baseAddress);http.createServer(httpConnection).listen(redirectAddress);https.createServer(httpsOptions, httpsConnection).listen(httpsAddress);function tcpConnection(conn) { conn.once('data', function (buf) { // A TLS handshake record starts with byte 22. var address = (buf[0] === 22) ? httpsAddress : redirectAddress; var proxy = net.createConnection(address, function () { proxy.write(buf); conn.pipe(proxy).pipe(conn); }); });}function httpConnection(req, res) { var host = req.headers['host']; res.writeHead(301, { "Location": "https://" + host + req.url }); res.end();}function httpsConnection(req, res) { res.writeHead(200, { 'Content-Length': '5' }); res.end('HTTPS');}
As a test, If you connect it with https you get the https handler:
$ curl https://127.0.0.1:3000 -kHTTPS
if you connect it with http you get the redirect handler (which simply takes you to the https handler):
$ curl http://127.0.0.1:3000 -iHTTP/1.1 301 Moved PermanentlyLocation: https://127.0.0.1:3000/Date: Sat, 31 May 2014 16:36:56 GMTConnection: keep-aliveTransfer-Encoding: chunked
If serving HTTP and HTTPS over a single port is an absolute requirement you can proxy the request to the relevant HTTP implementation directly, rather than piping the socket to another port.
httpx.js
'use strict';let net = require('net');let http = require('http');let https = require('https');exports.createServer = (opts, handler) => { let server = net.createServer(socket => { socket.once('data', buffer => { // Pause the socket socket.pause(); // Determine if this is an HTTP(s) request let byte = buffer[0]; let protocol; if (byte === 22) { protocol = 'https'; } else if (32 < byte && byte < 127) { protocol = 'http'; } let proxy = server[protocol]; if (proxy) { // Push the buffer back onto the front of the data stream socket.unshift(buffer); // Emit the socket to the HTTP(s) server proxy.emit('connection', socket); } // As of NodeJS 10.x the socket must be // resumed asynchronously or the socket // connection hangs, potentially crashing // the process. Prior to NodeJS 10.x // the socket may be resumed synchronously. process.nextTick(() => socket.resume()); }); }); server.http = http.createServer(handler); server.https = https.createServer(opts, handler); return server;};
example.js
'use strict';let express = require('express');let fs = require('fs');let io = require('socket.io');let httpx = require('./httpx');let opts = { key: fs.readFileSync('./server.key'), cert: fs.readFileSync('./server.cert')};let app = express();app.use(express.static('public'));let server = httpx.createServer(opts, app);let ws = io(server.http);let wss = io(server.https);server.listen(8080, () => console.log('Server started'));
I know its an old question but just putting it as a reference for someone else.The easiest way that I found was to use the https://github.com/mscdex/httpolyglot module. Seems to do what it says quite reliably
var httpolyglot = require('httpolyglot'); var server = httpolyglot.createServer(options,function(req,res) { if (!req.socket.encrypted) { // Redirect to https res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url }); res.end(); } else { // The express app or any other compatible app app.apply(app,arguments); } }); // Some port server.listen(11000);