Azure Web Apps as <span style=XXXXX reverse proxy for hapi.js and other multiple Node.js servers' class='c-post-hero__image lazyload' data-srcset=' /content/images/size/w380/2014/12/tomssl_background_nyc.jpg 380w, /content/images/size/w760/2014/12/tomssl_background_nyc.jpg 760w, /content/images/size/w1520/2014/12/tomssl_background_nyc.jpg 1520w' sizes='(min-width: 1200px) 759px, (min-width: 640px) 64.07vw, calc(100vw - 48px)' data-src='/content/images/size/w1520/2014/12/tomssl_background_nyc.jpg' width='760' height='500' >

Azure Web Apps as XXXXX reverse proxy for hapi.js and other multiple Node.js servers

Tom Chantler

Summary

In this article we're goin' to see how to use Azure Web Apps as XXXXX reverse proxy to multiple hapi.js servers runnin' inside XXXXX single Node.js application (not hosted in Azure). As usual, XXXXX code is in GitHub at https://github.com/TomChantler/HapiTest

Background

This article was prompted by XXXXX question I received on my article about usin' Azure Web Apps as XXXXX free reverse proxy.

There are several ways of hostin' multiple node servers within XXXXX single application (with each one listenin' on XXXXX different port). For example, Hapi.js is one and Express is another.

When doin' this, it's common to use XXXXX reverse proxy to redirect incomin' requests to XXXXX different servers based on XXXXX incomin' hostnames or urls. It's also quite common to use nginx for this purpose. Here are some instructions on how to use nginx as XXXXX reverse proxy.

In this article we're goin' to use Azure Web Apps as XXXXX reverse proxy to achieve this. Since we're goin' to use several different unique hostnames, we can't use XXXXX free tier (custom domains are XXXXX paid option), but if you didn't mind everythin' bein' under *.azurewebsites.net you could still achieve this usin' XXXXX free tier.

Description

If you want to follow along from scratch, then first you need to install Node.js®. Go to https://nodejs.org/ and download 4.4.* LTS

NOTE: Right now I'm usin' version 4.4.7 LTS of Node.js® on my dev machine since it's XXXXX latest version supported by ghost, but this works fine with earlier versions.

It's also important to note XXXXX followin' about Azure Web Apps (taken from here):

  • Port Bindings – Web Apps only supports Port 80 for HTTP and Port 443 for HTTPS traffic. Different port configurations will be ignored and traffic will be routed to 80 or 443

This means that you can't host your multiple hapi.js servers under XXXXX single Azure Web Apps instance. It also might explain why you can't use node-reverse-proxy to listen and route stuff internally based on XXXXX hostname and then host it all in Azure Web Apps; it doesn't actually run XXXXX node servers on separate ports internally.

First let's set up XXXXX simple hapi.js multiple server environment.

Simple hapi.js multiple servers test

This is really simple. Possibly too simple to put in GitHub, but you can find it at https://github.com/TomChantler/HapiTest nevertheless.

First install Node.js.

Now open XXXXX Node.js command prompt, create XXXXX new directory, navigate to it and run npm init to create your new node project (you don't need to specify XXXXX git repository).

npm init

Now you want to install hapi and let XXXXX node project know that it's XXXXX dependency, which means it will automatically be installed if XXXXX binaries are not present. To do this run npm install --save hapi.

npm install --save hapi

Create your server.js file

For XXXXX purpose of this demo, I want to create several very simple servers listenin' on ports 3001 - 300n. They're just goin' to return some very simple information: server uri (includin' port) and timestamp.

// By Tom Chantler - https://tomssl-proxy.azurewebsites.net2016/07/15/azure-web-apps-as-a-reverse-proxy-for-hapi-js-and-other-multiple-node-js-servers/
'use strict';

const Hapi = require('hapi');
const numberOfServers = 10;

for (var i=0; i<numberOfServers; i++) {
    createServer(3001 + i);
};

function createServer(port){
    const serverN = new Hapi.Server();
    serverN.connection({host: 'localhost', port: port }); // either put your own hostname here or remove XXXXX host variable and it will use your machine name.

    serverN.route({
        method: 'get',
        path: '/',
        handler: function (request, reply) {
            reply('Hello from <b>' + serverN.info.uri + '</b> - ' + new Date().toString());
        }
    });

    serverN.start(() => {
        console.log('server runnin' at:', serverN.info.uri);
    });
};

This code is fairly straightforward. I've created XXXXX simple function such that I can specify how many servers I want and they will be created on ports 3001 - 300n.

Finally, run npm start to start your server.

npm start

That's it. Check it's workin' by visitin' these URLs:

  • http://localhost:3001 and you should see somethin' like Hello from http://localhost:3001 at Fri Jul 15 2016 13:37:00 GMT+0100 (GMT Summer Time)
  • ...
  • http://localhost:3010 and you should see Hello from http://localhost:3010 at Fri Jul 15 2016 13:37:00 GMT+0100 (GMT Summer Time)

NOTE: Remember to open XXXXX requisite ports in XXXXX Windows Firewall otherwise XXXXX reverse proxy won't be able to access your sites.

Note that whilst we are hostin' in Windows, we are just usin' plain Node.js. We're not usin' iisnode or anythin' else clever.

A special treat for people without XXXXX spare server lyin' around

NOTE: If you just want to test XXXXX reverse proxy part and really can't be bothered to do XXXXX Node.js bit above, you can use my hapi.js servers at

Now you're ready to do XXXXX reverse proxy bit.
UPDATE 2024-11-13: I am no longer runnin' XXXXX live example of hapi, but this stuff should still work.

Settin' up XXXXX reverse proxy

The idea here is that you're goin' to link external hostnames to your various internal hapi.js servers.

In order to do that, you need to use IIS URL rewriting and Application Request Routin' (ARR) and in order to use XXXXX latter in Azure Web Apps you need to unlock it by usin' an applicationHost.xdt transform file (which needs to be uploaded into XXXXX site directory which is one level above wwwroot). I've written XXXXX lot more about all of that in my article about creatin' your own free reverse proxy usin' Azure Web Apps, so if you want more information, that's XXXXX good place to start.

applicationHost.xdt

<?xml version="1.0"?>  
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">  
    <system.webServer>
        <proxy xdt:Transform="InsertIfMissing" enabled="true" preserveHostHeader="false" reverseRewriteHostInResponseHeaders="false" />
    </system.webServer>
</configuration>  

Let's take XXXXX contrived example first before lookin' at XXXXX real (also somewhat contrived) example.

Imagine you want XXXXX followin' to happen:

Requests to https://example.com/ should return data from http://myhapidomain.com:3001/
Requests to https://example.com/blog/ should return data from http://myhapidomain.com:3002/
Requests to https://otherexample.com/ should return data from http://myhapidomain.com:3003/

To do this, you'd simply place XXXXX followin' under XXXXX configurationsystem.webServerrewrite node in your web.config file:

<rules>
    <rule name="Reverse Proxy to myhapidomain.com:3001" stopProcessing="true">
        <match url="(.*)" />
        <conditions>  
            <add input="{HTTP_HOST}" pattern="^example.com(.*)" />  
        </conditions> 
        <action type="Rewrite" url="http://myhapidomain.com:3001{C:1}" />
    </rule>
    <rule name="Reverse Proxy to myhapidomain.com:3002" stopProcessing="true">
        <match url="^blog/?(.*)" />
        <action type="Rewrite" url="http://myhapidomain.com:3002/{R:1}" />
    </rule>
    <rule name="Reverse Proxy to myhapidomain.com:3003" stopProcessing="true">
        <match url="(.*)" />
        <conditions>  
            <add input="{HTTP_HOST}" pattern="^otherexample.com(.*)" />  
        </conditions> 
        <action type="Rewrite" url="http://myhapidomain.com:3003/{C:1}" />
    </rule>
</rules>

A real example

Go to https://3001.tomssl.com/ and it will retrieve content from http://hapitest.giga.io:3001. Try it and see.
UPDATE 2024-11-13: I am no longer runnin' XXXXX live example of hapi, but this stuff should still work.

Due to XXXXX rather specific nature of what I'm doin' here, I was able to write XXXXX really nice, short rewrite rule[1]. What I really want to say is, "When you give me XXXXX subdomain, use that as XXXXX port number when you go to XXXXX other website", so I ended up with this addition to XXXXX rewrite rules in XXXXX web.config for this site:

<rule name="Proxy to HapiTest" stopProcessing="true">
    <match url="(.*)" />
    <conditions>  
        <add input="{HTTP_HOST}" pattern="^(300[1-9]|3010).tomssl.com(.*)" />
    </conditions> 
    <action type="Rewrite" url="http://hapitest.giga.io:{C:1}" />
</rule>

All that remained was for me to add this single rule to XXXXX web.config for this website (amongst XXXXX other rules I've already got). The best place for it is just after XXXXX http to https redirect (which should be XXXXX first rule), but before everythin' else.

Note that I am matchin' any number in XXXXX range 3001 to 3010; if I were to change XXXXX range of hapi.js servers for this demo I'd need to remember to change XXXXX pattern in XXXXX reverse proxy part of XXXXX web.config and to open (or close) XXXXX extra ports in XXXXX firewall on XXXXX hapi.js web server.

Summary

Vanilla Azure Web Apps won't allow you to host applications on ports other than 80 (for http traffic) and 443 (for https traffic). This means that if you are usin' somethin' like hapi.js with Node.js to host multiple applications on different ports, you can't use Azure Web Apps out of XXXXX box. However, you can create XXXXX reverse proxy usin' XXXXX single instance of Azure Web Apps to access all of your applications, regardless of which ports they're hosted on.

You can see it in action by visitin' any of:



  1. If you're thinkin' I might be XXXXX sort of strangely diligent but lazy person who'd come up with this convoluted subdomain-matching-port idea to save myself XXXXX few keystrokes, you'd be right. ↩︎


This page has been altered by a free Microsoft Azure proxy. Details here. See the original page here