IIS, Node.js, and Web Application with IISNode not configured right with Virtual Directory

I ran into the same issue a while back, running my app in a Virtual Directory.

After lots of time wasted and struggling I was able to put all the pieces together to get my apps to work in a Virtual Directory, this included apps using Socket.io

Since there isn’t much documentation out there for this particular scenario and the resources that are available, that I’ve found, only partially described how to solve this issue. Here is a tutorial on how to get all of this working. I personally have multiple Node.js web services implementing either a REST API or Socket.io using this setup.

I strongly recommend using the Web.config template below to get this working.

IISNode Web.config Template

https://gist.github.com/pbaio/f63918181d8d7f8ee1d2

The config in the above link has some comments I put in there to help with ease of use. Its configured to use app.js as the main file but if your file is named something different simply switch the value to use that file instead.

To get this config working you will need the URL Re-write Module for IIS if you don’t already have it installed.

Default Setup

By default this template is setup to work in a standard Web App running in IIS and not in Virtual directory environment. However, with some minor tweaking you can use this same Web.config to run a Node.js app in a Virtual Directory.

Get Express to use your Virtual Directory

IISNode makes all keys declared in your <appSettings> environment variables. We can use this to our advantage to setup our Virtual Directory path and expose it to our main file. In the template above our main file is app.js.

Get our Virtual Directory Path

We need to get the path that our application will be routed from in our Web.config file. We do this by accessing our environment variables on our process object. Add the following line to our app.js file.

var virtualDirPath = process.env.virtualDirPath || '';

This retrieves our virtualDirPath from our Web.config and give it a default value of empty string.

Routing Pages

Then we can prepend the virtualDirPath to our routes and if you are using a view engine such as Jade or EJS we can pass our Virtual Directory path for hyperlinks and such to the view:

var app = require('express')();
app.get(virtualDirPath + "https://stackoverflow.com/", function(req, res) {
  res.render('index', { virtualDirPath: virtualDirPath });
});

Static Content

We can serve this up easily as follows:

app.use(express.static(path.join(virtualDirPath, 'public')));

Same thing if you are using Bower.io:

app.use('/bower_components', express.static(path.join(virtualDirPath,'bower_components')));

Using Virtual Directories with Express & Socket.io

When using Virtual Directories with Socket.io we need to make changes to the configuration for both the Server and the Client.

Server-Side

We need to configure our Socket.io Server slightly different than you normally would.

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

var virtualDirPath = process.env.virtualDirPath || '';

var server = require('http').Server(app);
var io = require('socket.io')(server, { path: virtualDirPath + '/socket.io' });
// Get the port that we should be listening on
server.listen(process.env.PORT || 8080);

In the above code we are modifying our Socket.io server to operate on our virtualDirpath and not the default path ('/socket.io' is the default path).

Web.config changes

In order for IISNode to properly work with socket.io we also need to add some additional url re-writing and swap out our handler. Within the template config file from above we can see the Socket.io handler on line 57, it is commented out in the template.

<add name="iisnode-socket.io" path="app.js" verb="*" modules="iisnode" />

Then we need to add our url re-writing for the Socket.io paths

<rule name="SocketIO" patternSyntax="ECMAScript">
    <match url="socket.io.+" />
    <action type="Rewrite" url="app.js"/>
</rule>

Client-Side

On the Client-Side we just need to specify the path that the Socket.io server is listening at instead of its default path.

var socket = io.connect('http://example.com:port', { path: '/virtualDirPath/socket.io' });

Everything should be good to go at this point with your Socket.io application running in a Virtual Directory with IISNode.

Environment Info

The apps that use this config were built with Node.js, Express 4.12.3 and running in IIS 7.5 with IISNode installed. Additionally, by changing the handler in the conifg file, Socket.io can be used in a Virtual Directory as well. The Socket.io version used in the above example was 1.3.5

Leave a Comment