How to ProxyPass WebSocket Connections to UNIX Sockets in Apache 2.4


10 views

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:

  1. Check Apache error logs (tail -f /var/log/apache2/error.log)
  2. Verify socket permissions with ls -la /path/to/socket
  3. Test socket connectivity with nc -U /path/to/socket
  4. 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