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');
});