Apache's mod_proxy
and mod_proxy_wstunnel
modules provide excellent reverse proxy capabilities, but there's a gap when it comes to WebSocket connections over UNIX domain sockets. While HTTP proxying to UNIX sockets is straightforward:
ProxyPass unix:/path/to/app.sock|http://example.com/app/name
And WebSocket proxying to TCP ports is well-documented:
ProxyPass ws://127.0.0.1:12345/app/name
The combination of WebSocket + UNIX socket proxying requires some additional configuration.
To make this work, we need to combine several Apache modules and use the Proxy
directive with proper WebSocket upgrade handling:
# First, ensure required modules are loaded
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule proxy_http_module modules/mod_proxy_http.so
# Then configure the WebSocket proxy
<Location "/app/ws/">
ProxyPass "unix:/path/to/ws.sock|ws://dummy-host/app/ws/"
ProxyPassReverse "unix:/path/to/ws.sock|ws://dummy-host/app/ws/"
# Required for WebSocket upgrade
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule .* "unix:/path/to/ws.sock|ws://dummy-host%{REQUEST_URI}" [P]
</Location>
Here's a full virtual host configuration that handles both HTTP and WebSocket traffic:
<VirtualHost *:80>
ServerName example.com
# Enable proxy modules
ProxyRequests Off
ProxyPreserveHost On
ProxyVia On
# HTTP proxy
ProxyPass "/app/http/" "unix:/path/to/http.sock|http://dummy-host/app/http/"
ProxyPassReverse "/app/http/" "unix:/path/to/http.sock|http://dummy-host/app/http/"
# WebSocket proxy
<Location "/app/ws/">
ProxyPass "unix:/path/to/ws.sock|ws://dummy-host/app/ws/"
ProxyPassReverse "unix:/path/to/ws.sock|ws://dummy-host/app/ws/"
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule .* "unix:/path/to/ws.sock|ws://dummy-host%{REQUEST_URI}" [P]
</Location>
</VirtualHost>
- The
dummy-host
in the configuration is just a placeholder and doesn't need to resolve - Ensure your UNIX socket has proper permissions for Apache to access it
- WebSocket applications must be configured to listen on the UNIX socket
- Test with
curl --unix-socket
or WebSocket clients that support UNIX sockets
If you encounter issues:
- Check Apache error logs (
tail -f /var/log/apache2/error.log
) - Verify socket permissions with
ls -la /path/to/socket
- Test socket connectivity with
nc -U /path/to/socket
- Enable debug logging in Apache with
LogLevel debug
Apache's mod_proxy
and mod_proxy_wstunnel
modules provide excellent reverse proxying capabilities, but there's a gap when dealing with WebSocket connections targeting UNIX domain sockets. While HTTP proxying to UNIX sockets is straightforward, WebSocket connections require special handling.
For HTTP traffic, Apache supports UNIX socket proxying via:
ProxyPass unix:/path/to/app.sock|http://example.com/app/name
For WebSocket over TCP, the syntax is:
ProxyPass ws://127.0.0.1:12345/app/name
The current documentation doesn't explicitly mention WebSocket support for UNIX domain sockets. However, we can combine these approaches with some configuration tweaks.
Here's how to properly proxy WebSocket connections to a UNIX socket:
# Enable required modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
# Configuration for WebSocket over UNIX socket
<Location "/ws-app/">
ProxyPass "unix:/path/to/ws.sock|ws://dummy-host/ws-app/"
ProxyPassReverse "unix:/path/to/ws.sock|ws://dummy-host/ws-app/"
ProxyPreserveHost On
Require all granted
</Location>
1. The dummy hostname in the ws://
URL is ignored when using UNIX sockets
2. Socket file permissions must allow Apache's user to read/write
3. Your backend application must properly handle WebSocket protocol handshake
Here's a simple Python example using websockets
library that works with this setup:
import asyncio
import websockets
import os
async def handler(websocket, path):
while True:
try:
message = await websocket.recv()
await websocket.send(f"Echo: {message}")
except websockets.exceptions.ConnectionClosed:
break
async def main():
socket_path = "/path/to/ws.sock"
try:
os.unlink(socket_path)
except OSError:
pass
async with websockets.unix_serve(handler, socket_path):
await asyncio.Future() # run forever
asyncio.run(main())
- Check Apache error logs for connection issues
- Verify socket file exists after backend starts
- Use curl --unix-socket
for manual testing
- Ensure mod_proxy
and mod_proxy_wstunnel
are loaded