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.