Configuring Apache 2.4 mod_proxy_wstunnel for Socket.IO 1.0 WebSocket Proxying


2 views

When upgrading from Socket.IO 0.9 to 1.0, many developers face challenges with Apache reverse proxy configurations due to fundamental changes in the WebSocket connection URL pattern. The new version dynamically generates endpoint URLs with query parameters like EIO=3&transport=websocket, making traditional ProxyPass rules ineffective.

Standard Apache proxy configurations break because:

# This WON'T work with query parameters
ProxyPass /socket.io/ ws://localhost:8082/socket.io/
ProxyPassReverse /socket.io/ ws://localhost:8082/socket.io/

Apache's mod_proxy doesn't evaluate query strings when matching ProxyPass rules by default.

Here's the configuration that properly handles both HTTP and WebSocket traffic:

# WebSocket upgrade handling
RewriteEngine On
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:8082/$1 [P,L]

# Standard HTTP proxy
ProxyPass /socket.io/ http://localhost:8082/socket.io/
ProxyPassReverse /socket.io/ http://localhost:8082/socket.io/

The magic happens through these components:
1. RewriteCond checks for the WebSocket transport parameter
2. RewriteRule with [P] flag proxies the request
3. Regular HTTP traffic falls through to standard ProxyPass

For enhanced reliability, add these directives:

ProxyRequests Off
ProxyPreserveHost On
ProxyTimeout 300

If issues persist, enable these logging directives:

LogLevel rewrite:trace8
LogLevel proxy:trace8

Check for these common pitfalls:
- Missing mod_proxy_wstunnel module
- Incorrect rewrite rule ordering
- Firewall blocking WebSocket ports

For those preferring Location blocks:

<LocationMatch "^/socket.io/">
    RewriteEngine On
    RewriteCond %{QUERY_STRING} transport=websocket [NC]
    RewriteRule ^(.*)$ ws://localhost:8082/$1 [P,L]
    ProxyPass http://localhost:8082/socket.io/
    ProxyPassReverse http://localhost:8082/socket.io/
</LocationMatch>

For high-traffic applications:
- Consider adding connection pooling
- Adjust ProxyTimeout based on your ping interval
- Monitor mod_proxy memory usage under load


Socket.IO 1.0 introduced a breaking change in its WebSocket URL format, moving the protocol indicator to query parameters (?EIO=N&transport=websocket). This creates complications when trying to proxy these connections through Apache 2.4 using mod_proxy_wstunnel.

The main issue is that Apache's ProxyPass directive doesn't match against query strings. Your first attempt likely failed because:

# This won't work as expected
ProxyPass /socket.io/?EIO=2&transport=websocket ws://localhost:8082/socket.io/?EIO=2&transport=websocket

Here's the configuration that properly handles both WebSocket and HTTP polling transport:

<LocationMatch "^/socket.io/">
    RewriteEngine On
    RewriteCond %{QUERY_STRING} transport=websocket [NC]
    RewriteRule /(.*) ws://localhost:8082/$1 [P,L]
    
    ProxyPass http://localhost:8082/socket.io/
    ProxyPassReverse http://localhost:8082/socket.io/
</LocationMatch>

LocationMatch: Catches all paths starting with /socket.io/

RewriteCond: Checks for the WebSocket transport parameter

RewriteRule: Proxies WebSocket requests using ws:// protocol

ProxyPass: Handles all other (HTTP polling) requests

If you prefer using only ProxyPass directives:

# WebSocket route
ProxyPassMatch ^/socket.io/(.*)\?EIO=([0-9]+)&transport=websocket$ ws://localhost:8082/socket.io/$1

# All other Socket.IO traffic
ProxyPass /socket.io/ http://localhost:8082/socket.io/
ProxyPassReverse /socket.io/ http://localhost:8082/socket.io/

1. Ensure these modules are loaded:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule rewrite_module modules/mod_rewrite.so

2. The WebSocket upgrade headers must be preserved:

ProxyPassReverseCookieDomain localhost:8082 yourdomain.com
RequestHeader set X-Forwarded-Proto "https" env=HTTPS

To verify everything works:

1. Check Apache error logs for WebSocket connection attempts

2. Use browser developer tools to inspect the WebSocket handshake

3. Test with this simple Node.js server:

const io = require('socket.io')(8082);
io.on('connection', (socket) => {
    console.log('Client connected');
    socket.emit('message', 'Connected through Apache proxy');
});