How to proxy web sockets using Apache 2.14 and Node

pretty-hot-girl-working-on-a-laptop

I have an application that I am working on that requires the use of socket.io.  In most implementations, folks are using Node.JS to provide API access on the server-side.   However, many of us still use Apache for serving up static content, and using Node as the API server, facilitating this by using Apache’s proxy capability (mod-proxy).

However, when using the socket.io library, it becomes quickly apparent that a straight-forward proxy will not work.  If you watch how socket.io connects, it connects to a single URL, both using http polling, and then attempting to switch to web sockets if possible, using parameters passed on the query string to control this behavior.

websocket meme

In our implementation, we configure the client to use a specific socket path, which we can filter on in our proxy configuration.  As a bonus, we can also use this socket path to load-balance web socket traffic if required.

Socket.io setup in client side JavaScript.

var socketpath = ‘/chat_api/socket.io’;
var url = ‘/’;

socket = io.connect(url, {
path: socketpath
});

In the Apache config file, we configure ProxyPass to pass all normal http traffic to the node server, API calls (/api) going to one Node port, and Chat API calls (/chat_api)going to another port.  Our rewrite condition specifically looks for web socket traffic by matching the query parameter transport as matching websocket.  In this case, we do a Rewrite to ws:// node chat server.

Apache http.conf

RewriteEngine On

RewriteCond %{REQUEST_URI} ^/chat_api/socket.io [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule “^/chat_api/socket.io” “ws://localhost:4001/socket.io/” [P,L]

ProxyPass /api http://localhost:4000/api
ProxyPassReverse /api http://localhost:4000/api

ProxyPass /chat_api http://localhost:4001
ProxyPassReverse /chat_api http://localhost:4001

In my limited testing, I was ONLY able to get this to reliably work using Apache 2.4.16.  I believe that this is due to a bug in earlier versions that will not allow you to redirect to a web socket  (ws://).   I was forced to compile and install the most recent version available when I wrote this.

One of the most often recommended solutions is to attempt to use proxy_wstunnel and back-port it to an older version of Apache.  I tried this method, and was unsuccessful in getting a stable configuration.  Your milage may vary…

Tagged on: ,

Leave a Reply

Your email address will not be published. Required fields are marked *