107

I have a linux server with a single IP bound to it. I want to host multiple Node.js sites on this server on this IP, each (obviously) with a unique domain or subdomain. I want them all on port 80.

What are my options to do this?

An obvious solution seems to be to have all domains serviced by a node.js web app that acts as a proxy and does a pass through to the other node.js apps running on unique ports.

1
  • 4
    I do this and upstream with nginx, name based virtual hosting. As a bonus nginx can be configured to serve static files, do perm redirects, etc. Commented Oct 8, 2013 at 17:46

13 Answers 13

92

Choose one of:

  • Use some other server (like nginx) as a reverse proxy.
  • Use node-http-proxy as a reverse proxy.
  • Use the vhost middleware if each domain can be served from the same Connect/Express codebase and node.js instance.
3
  • 4
    that's a very good and brief list of the options I've read elsewhere. Do you happen to know for each of these solutions which processes would need to be restarted when a new domain is added? For 1) none. For 2) only the node-http-proxy. For 3) the entire thread of all sites would need to be restarted. Is this correct?
    – Flion
    Commented Feb 5, 2015 at 10:48
  • 1
    @Flion: You could write the node-based proxies in such a way that you could reload the domain configuration without requiring a process restart. It really depends on your app's exact requirements.
    – josh3736
    Commented Feb 5, 2015 at 17:50
  • Not what was asked. Commented Mar 18, 2020 at 7:47
59

Diet.js has very nice and simple way to host multiple domains with the same server instance. You can simply call a new server() for each of your domains.

A Simple Example

// Require diet
var server = require('diet');

// Main domain
var app = server()
app.listen('http://example.com/')
app.get('/', function($){
    $.end('hello world ')
})

// Sub domain
var sub = server()
sub.listen('http://subdomain.example.com/')
sub.get('/', function($){
    $.end('hello world at sub domain!')
})

// Other domain
var other = server()
other.listen('http://other.com/')
other.get('/', function($){
    $.end('hello world at other domain')
})

Separating Your Apps

If you would like to have different folders for your apps then you could have a folder structure like this:

/server
   /yourApp
       /node_modules
       index.js
   /yourOtherApp
       /node_modules
       index.js
   /node_modules
   index.js

In /server/index.js you would require each app by it's folder:

require('./yourApp')
require('./yourOtherApp')

In /server/yourApp/index.js you would setup your first domain such as:

// Require diet
var server = require('diet')

// Create app
var app = server()
app.listen('http://example.com/')
app.get('/', function($){
    $.end('hello world ')
})

And in /server/yourOtherApp/index.js you would setup your second domain such as:

// Require diet
var server = require('diet')

// Create app
var app = server()
app.listen('http://other.com/')
app.get('/', function($){
    $.end('hello world at other.com ')
});

Read More:

5
  • This comment saved all my problems :) ... even in 2016
    – myTerminal
    Commented Sep 18, 2016 at 14:44
  • Will this reload automatically ? I started using PM2 but it doesn't allow me to host multiple domain and reverse proxy Commented May 21, 2018 at 8:39
  • Does this replace express, or work alongside it? My guess is either. But I want to be sure before I create a frankenstein.
    – Mardok
    Commented Jan 7, 2019 at 14:46
  • 5
    FYI As of July 2019 - dietjs.com isn't up anymore and there hasn't been an update to the github codebase in ~2 years.
    – sirclesam
    Commented Jul 16, 2019 at 2:33
  • Is there a modern way of doing this?
    – Cullub
    Commented Jan 2, 2023 at 16:40
25

Hm ... why you think that nodejs should act as a proxy. I'll suggest to run several node apps listening on different ports. Then use nginx to forward the request to the right port. If use a single nodejs you will have also single point of failure. If that app crashes then all the sites go down.

16

Use nginx as a reverse proxy.

http://www.nginxtips.com/how-to-setup-nginx-as-proxy-for-nodejs/

Nginx brings a whole host of benefits to your applications in the form of caching, static file handling, ssl and load balancing.

14

I have an API I use on a site and below is my configuration. I also have it with SSL and GZIP, if someone needs it, just comment me.

var http = require('http'),
    httpProxy = require('http-proxy');

var proxy_web = new httpProxy.createProxyServer({
        target: {
            host: 'localhost',
            port: 8080
        }
    });

    var proxy_api = new httpProxy.createProxyServer({
        target: {
            host: 'localhost',
            port: 8081
        }
    });

    http.createServer(function(req, res) {
        if (req.headers.host === 'http://www.domain.com') {
            proxy_web.proxyRequest(req, res);
            proxy_web.on('error', function(err, req, res) {
                if (err) console.log(err);
                res.writeHead(500);
                res.end('Oops, something went very wrong...');
            });
        } else if (req.headers.host === 'http://api.domain.com') {
            proxy_api.proxyRequest(req, res);
            proxy_api.on('error', function(err, req, res) {
                if (err) console.log(err);
                res.writeHead(500);
                res.end('Oops, something went very wrong...');
            });
        }
    }).listen(80);
0
7

If you are using connect/express server, you can see the vhost middleware. It will allow multiple domains(sub-domains) to be used for the server address.

You can follow the example given here, which looks exactly like what you need.

7

Here's how to do it using vanilla Node.js:

const http = require('http')
const url = require('url')
const port = 5555
const sites = {
  exampleSite1: 544,
  exampleSite2: 543
}

const proxy = http.createServer( (req, res) => {
  const { pathname:path } = url.parse(req.url)
  const { method, headers } = req
  const hostname = headers.host.split(':')[0].replace('www.', '')
  if (!sites.hasOwnProperty(hostname)) throw new Error(`invalid hostname ${hostname}`)

  const proxiedRequest = http.request({
    hostname,
    path,
    port: sites[hostname],
    method,
    headers 
  })

  proxiedRequest.on('response', remoteRes => {
    res.writeHead(remoteRes.statusCode, remoteRes.headers)  
    remoteRes.pipe(res)
  })
  proxiedRequest.on('error', () => {
    res.writeHead(500)
    res.end()
  })

  req.pipe(proxiedRequest)
})

proxy.listen(port, () => {
  console.log(`reverse proxy listening on port ${port}`)
})

Pretty simple, huh?

0
4

First install forever and bouncy.

Then write a startup script. In this script, add a rule to the iptables firewall utility to tell it to forward the traffic on port 80 to port 8000 (or anything else that you choose). In my example, 8000 is where I run bouncy

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000

Using forever, let's tell the script to run bouncy on port 8000

forever start --spinSleepTime 10000 /path/to/bouncy /path/to/bouncy/routes.json 8000

The routes.json would something like

{
    “subdomain1.domain.com" : 5000,
    “subdomain2.domain.com" : 5001,
    “subdomain3.domain.com" : 5002
}

NodeJS application1, application2 and application3 run on port 5000, 5001 and 5002 respectively.

The script I use in my case can be found here and you might have to change a little to fit in your environment.

I also wrote about this in more details and you can find it here.

3
  • You should check PM2 Commented May 21, 2018 at 8:42
  • ok this is great, but how can I force http-> https in bouncy??
    – user9601534
    Commented Aug 8, 2019 at 17:55
  • What about certificates for each domain? Commented Jul 10 at 10:30
3

This is my simplest demo project without any middleware or proxy.
This requires only a few codes, and it's enough.

https://github.com/hitokun-s/node-express-multiapp-demo

With this structure, you can easily set up and maintain each app independently.
I hope this would be a help for you.

3
+100

Literally, when you get the request and response object, you can get the domain through "request.headers.host"... (not the IP address, actually the domain).

0
2

Based on @Michaaatje and @papiro, a very easy way:

Say you have some typical pages such as...

var app = express()
app.use(sess)
app.use(passport.initialize())
app.use(passport.session())
app.use('/static', express.static('static'))

app.get('/', ensureLoggedIn("/loginpage"), function(req, res, next) {
    ...
})

app.get('/sales', ensureLoggedIn("/loginpage"), function(req, res, next) {
    ...
})

app.get('/about', ensureLoggedIn("/loginpage"), function(req, res, next) {
    ...
})

app.post('/order', ensureLoggedIn("/loginpage"), urlencodedParser, (req, res) => {
    ...
})

.. and so on.

Say the main domain is "abc.test.com"

But you have an "alternate" domain (perhaps for customers) which is "customers.test.com".

Simply add this ...

var app = express()
app.use(sess)
app.use(passport.initialize())
app.use(passport.session())
app.use('/static', express.static('static'))

app.use((req, res, next) => {
    req.isCustomer = false
    if (req.headers.host == "customers.test.com") {
        req.isCustomer = true
    }
    next();
})

and then it is this easy ...

app.get('/', ensureLoggedIn("/loginpage"), function(req, res, next) {
    if (req.isCustomer) {
        .. special page or whatever ..
        return
    }
    ...
})

app.get('/sales', ensureLoggedIn("/loginpage"), function(req, res, next) {
    if (req.isCustomer) {
        res.redirect('/') .. for example
        return
    }
    ...
})

app.get('/about', ensureLoggedIn("/loginpage"), function(req, res, next) {
    if (req.isCustomer) { ... }
    ...
})

app.post('/order', ensureLoggedIn("/loginpage"), urlencodedParser, (req, res) => {
    if (req.isCustomer) { ... }
    ...
})

Thanks to @Michaaatje and @papiro .

0

This guide from digital ocean is an excellent way. It uses the pm2 module which daemonizes your app(runs them as a service). No need for additional modules like Forever, because it will restart your app automatically if it crashes. It has many features that help you monitor the various applications running on your server. It's pretty awesome!

3
  • 1
    Does PM2 listen on port 80?
    – Guy
    Commented Oct 13, 2020 at 19:42
  • @Guy At the bottom of the guide is a section on setting up a reverse proxy with nginx that will expose pm2 to port 80
    – Isaac Pak
    Commented Oct 13, 2020 at 22:15
  • I'm using PM2 on many projects. It's a great product. I just misunderstood your answer as the original ask here was a bit different and I thought you were implying that it filled that need. thanks.
    – Guy
    Commented Oct 14, 2020 at 23:34
0

This can be done super easily by redbird. Suppose you have two domain names example.com and example1.com and two website serving on port 8000 and 8001.

You can set a reverse proxy to the two websites by the following scripts.

var proxy = require('redbird')({port: 80});
proxy.register("example.com", "http://localhost:8000");
proxy.register("example1.com", "http://localhost:8001");

You can use process management tool such as pm2 to run the scripts so that it will continue serving after you close your shell.

Not the answer you're looking for? Browse other questions tagged or ask your own question.