Nodejitsu

Save time managing and deploying your node.js app. Code faster with jitsu and npm

Proxying HTTP and Websockets in Node

About the author

Name
Location
Worldwide
nodejitsu nodejitsu

Here at Nodejitsu, we believe strongly in supporting the thriving open-source community around us, and one of our more popular open-source tools is node-http-proxy. It is full-featured, robust, and extremely easy to use - not to mention battle-hardened by ongoing production use at nodejitsu.com. The request that loaded this page, for example, was routed to our blog by http-proxy.

This guide is geared toward beginners and people who are unfamiliar with reverse HTTP proxying, websocket proxing, load balancing, virtual host configuration, request forwarding, and other web proxying concepts - those who already know what they're doing and just want to see the syntax should skip down to the sample code.


When should I use http-proxy?

Let's go over a very basic use case: you have one server, but you have more than one application hosted, each of which needs to handle requests for a specific domain.

The solution is to set up a proxy that listens for connections on one public port (i.e. 80), and then proxies the requests it receives to the proper application (each on its own non-public port) based on the hostname in each request.

But that sounds complicated!

Not for you it isn't! The http-proxy API is extremely simple, and you'll be proxying requests in no time at all. First, though, you'll need to install http-proxy. The recommended installation method is via npm.
If you don't have npm yet:

 curl http://npmjs.org/install.sh | sh

Once npm is installed (or if you have it already) the simplest installation is as follows:

 cd myapp
 npm install http-proxy

This will install http-proxy to myapp/node_modules/http-proxy - once that's done, it's time to write some code! Let's look at the simplest kind of proxy first:

var http = require('http'),  
    httpProxy = require('http-proxy');
//
// Create a basic proxy server in one line of code...
//
// This listens on port 8000 for incoming HTTP requests 
// and proxies them to port 9000
httpProxy.createServer(9000, 'localhost').listen(8000);

//
// ...and a simple http server to show us our request back.
//
http.createServer(function (req, res) {  
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
  res.end();
}).listen(9000);

Wait... really? Just one line of code?

Yes, really: one line of code to send all the requests that hit localhost:9000 to the waiting http server at localhost:8000, without requiring you to expose localhost:8000 directly.

This doesn't solve our problem, though: this code will proxy all the requests to the same place, rather than routing them by hostname. Don't worry: adding the extra functionality we need will be easy.

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

//
// Just set up your options...
//
var options = {  
  hostnameOnly: true,
  router: {
    'domainone.com': '127.0.0.1:9000',
    'domaintwo.net': '127.0.0.1:9001',
    'domainthree.org': '127.0.0.1:9002'
  }
}

//
// ...and then pass them in when you create your proxy.
//
var proxyServer = httpProxy.createServer(options).listen(80);  

That's all the code you need to route to different ports/apps based on domain. It's also possible to leave out the hostnameOnly option and route based on path as well:

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

//
//Leave out the hostnameOnly field this time, or set it to false...
//
var options = {  
  router: {
    'domainone.com/appone': '127.0.0.1:9000',
    'domainone.com/apptwo': '127.0.0.1:9001',
    'domaintwo.net/differentapp': '127.0.0.1:9002'
  }
}


//
//...and then pass in your options like last time.
//
var proxyServer = httpProxy.createServer(options).listen(80);  

That's it! It's really that easy. Set up your options, and you're only one line of code away from a battle-hardened, production-proven reverse HTTP proxy.

But what if I want to set up custom proxying logic?

No problem - http-proxy still has your back.

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

//
// Create a proxy server with custom application logic
//
httpProxy.createServer(function (req, res, proxy) {  
  //
  // Put your custom server logic here
  //

  if (req.url.match(/apples/)) {
    req.url = req.url.replace(/apples/, 'pears');
  }

  proxy.proxyRequest(req, res, {
    host: 'localhost',
    port: 9000
  });
}).listen(8000);

http.createServer(function (req, res) {  
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.write('request successfully proxied: ' + req.url +'\n' + JSON.stringify(req.headers, true, 2));
  res.end();
}).listen(9000);

That example was a little contrived, wasn't it?

Fine, smarty-pants - how's a round-robin load-balancing strategy for you?

var httpProxy = require('http-proxy');  
//
// A simple round-robin load balancing strategy.
// 
// First, list the servers you want to use in your rotation.
//
var addresses = [  
  {
    host: 'ws1.0.0.0',
    port: 80
  },
  {
    host: 'ws2.0.0.0',
    port: 80
  }
];

httpProxy.createServer(function (req, res, proxy) {  
  //
  // On each request, get the first location from the list...
  //
  var target = addresses.shift();

  //
  // ...then proxy to the server whose 'turn' it is...
  //
  proxy.proxyRequest(req, res, target);

  //
  // ...and then the server you just used becomes the last item in the list.
  //
  addresses.push(target);
});

// Rinse; repeat; enjoy.

That was cool, but I'm still not impressed.

Well how about proxying socket.io websockets? Something like this can help socket.io to scale properly:

var sys = require('sys'),  
    http = require('http'),
    colors = require('colors'),
    websocket = require('./../vendor/websocket'),
    httpProxy = require('node-http-proxy');

var utils = require('socket.io/lib/socket.io/utils'),  
    io = require('socket.io');

//
// Create the target HTTP server...
//
var server = http.createServer(function (req, res) {  
  res.writeHead(200);
  res.end();
});
server.listen(8080);

//
// ...now setup socket.io on the target HTTP server to handle websocket requests...
//
var socket = io.listen(server);  
socket.on('connection', function (client) {  
  sys.debug('Got websocket connection');

  client.on('message', function (msg) {
    sys.debug('Got message from client: ' + msg);
  });

  socket.broadcast('from server');
});

//
// ...next, create a proxy server with node-http-proxy...
//
var proxy = httpProxy.createServer(8080, 'localhost');  
proxy.listen(8081);

//
// ...and setup the web socket to wait for connections from our proxy.
//
var ws = new websocket.WebSocket('ws://localhost:8081/socket.io/websocket/', 'borf');

ws.on('open', function () {  
  ws.send(utils.encode('from client'));
});

ws.on('message', function (msg) {  
  sys.debug('Got message: ' + utils.decode(msg));
});

For further information on http-proxy, please visit the node-http-proxy github page.