Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listen on HTTP and HTTPS for a single express app

Can I create an Express server listening on both HTTP and HTTPS, with the same routes and the same middlewares?

Currently I do this with Express on HTTP, with stunnel tunneling HTTPS to Express, but I prefer a pure Node solution.

I can do it with this code, but using the handle method that is marked as private:

var express = require( 'express' )
    , https = require("https")
    , fs = require( 'fs' );

var app = express.createServer();
// init routes and middlewares
app.listen( 80 );

var privateKey = fs.readFileSync( 'privatekey.pem' ).toString();
var certificate = fs.readFileSync( 'certificate.pem' ).toString();
var options = {key: privateKey, cert: certificate};
https.createServer( options, function(req,res)
{
    app.handle( req, res );
} ).listen( 443 );
like image 336
Jazz Avatar asked Dec 02 '11 11:12

Jazz


People also ask

Can http and https run on same port?

It's possible to serve both HTTP and HTTPS on the same port. A TLS handshake record starts with byte 22, so you can use that to determine which protocol the client is trying to speak.

Do I need HTTP with Express?

Express does not have the ability to create a properly configured https server for you automatically. @user3497437 - I added more to my answer to show you two examples that are identical in function. You would typically only need to use http.

How do I get HTTP server from Express?

The usual two ways to start a server for use with Express are this: var express = require('express'); var app = express(); // Express creates a server for you and starts it var server = app. listen(80);


5 Answers

To enable your app to listen for both http and https on ports 80 and 443 respectively, do the following

Create an express app:

var express = require('express');
var app = express();

The app returned by express() is a JavaScript function. It can be be passed to Node’s HTTP servers as a callback to handle requests. This makes it easy to provide both HTTP and HTTPS versions of your app using the same code base.

You can do so as follows:

var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
var app = express();

var options = {
  key: fs.readFileSync('/path/to/key.pem'),
  cert: fs.readFileSync('/path/to/cert.pem'),
  ca: fs.readFileSync('/path/to/ca.pem')
};

http.createServer(app).listen(80);
https.createServer(options, app).listen(443);

For complete detail see the doc

like image 189
cmd Avatar answered Oct 04 '22 18:10

cmd


As a possible update to this question, you might want to check out the changes here for express 3. The change document says:

The return value of express() is a JavaScript Function, encapsulating everything that makes an Express app tick. This means you can easily setup HTTP and HTTPS versions of your application by passing it to node's http.createServer() and https.createServer():

In Express 3, express.createServer() is now express()

Here is a complete example for express 3:

var fs = require('fs')
    , https = require('https')
    , http = require('http')
    , express = require('express')
    , keys_dir = 'keys/'
    , server_options = {
        key  : fs.readFileSync(keys_dir + 'privatekey.pem'),
        ca   : fs.readFileSync(keys_dir + 'certauthority.pem'),
        cert : fs.readFileSync(keys_dir + 'certificate.pem')
      }
    , app = express();
app.configure(function(){
  app.use(express.cookieParser());
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.session( { secret: '' } ));
  app.use(app.router);
});
app.configure('development',function(){
  app.use(express.static(__dirname + '/public'));
  app.use(express.errorHandler({dumpExceptions: true, showStack:true}));
  app.set('view options', { pretty: true });
});
app.get('/', function(req, res){
  res.send('Hello World!');
});
https.createServer(server_options,app).listen(7000);
http.createServer(app).listen(8000);
like image 39
SnapShot Avatar answered Oct 04 '22 17:10

SnapShot


You can share the implementation via something like:

var register = function (app) {
    // config middleware
    app.configure({

    });

    // config routes
    app.get(...);
};

var http = express.createServer();
register(http);
http.listen(80);

var https = express.createServer({ key: /* https properties */ });
register(https);
https.listen(443);
like image 39
Jan Jongboom Avatar answered Oct 04 '22 19:10

Jan Jongboom


You can use express and https in same port.

this works for me.

const express=require('express');
const app=express();
const cors=require('cors');
const path=require("path");
const routes=require('./routes/api');
const routerComplain=require('./routes/api');
const routerstores=require('./routes/api');
const routerstock=require('./routes/api');
const routerreport=require('./routes/api');
const routeritem=require('./routes/api');
const bodyParser=require('body-parser');
const routerRegister=require('./routes/api');
const mongoose=require('mongoose');
var server = require('http').Server(app);
var io = require('socket.io')(server);
require("dotenv").config();

mongoose.connect('mongodb://@@@@@@@@@@@@@@@@@',{ useNewUrlParser: true },(err)=>{
    if(!err){
        console.log('db connected')
    }else{
        console.log('error in db')
    }
});

mongoose.Promise = global.Promise;
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(cors({credentials: true, origin:'http://localhost:3000'}));
app.use(express.static(path.join(__dirname, "client", "build")))

app.use('/reg',routes);
app.use('/complain',routerComplain);
app.use('/register',routerRegister);
app.use('/stores',routerstores);
app.use('/reports',routerreport);
app.use('/stock',routerstock);
app.use('/items',routeritem);

app.get("*", (req, res) => {
    res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});

io.on('connection', function (socket) {
    socket.emit('news', { hello: 'world' });
    socket.on('my other event', function (data) {
      console.log(data);
    });
  })

const port = process.env.port||4000;

server.listen(port,function(){
    console.log('now listening for request');
});
like image 41
Charith Prasanna Avatar answered Oct 04 '22 18:10

Charith Prasanna


If you want to use the traditional two ports, one of the above solutions probably works, but using httpolyglot, you can really easily have http and https on the same port with the same middlewares.

https://github.com/mscdex/httpolyglot

Here's some skeleton code that worked for me:

var express = require('express');
var fs = require('fs');
var httpolyglot = require('httpolyglot');
var app = express();

const options = {
    key: fs.readFileSync("/etc/ssl/certs/key"),
    cert: fs.readFileSync("/etc/ssl/certs/cer.cer")
};

httpolyglot.createServer(options, app).listen(port);

and if you want http -> https forwarding, you can just add this middleware function before the createServer() call:

app.use(function(req, res, next) {
    if (!req.secure ) {
            res.redirect (301, 'https://' + req.hostname + ':port' + req.originalUrl);
    }
    next();
});

This can be set up on a custom port

like image 39
Peter Weeks Avatar answered Oct 04 '22 18:10

Peter Weeks