Skip to content

Latest commit

 

History

History
238 lines (189 loc) · 6.88 KB

File metadata and controls

238 lines (189 loc) · 6.88 KB

WebSocket & Proxy Configuration

✅ Apache Reverse Proxy (Port 443 → 5000)

Configuration Location

/etc/apache2/conf.d/userdata/std/2_4/ishaglcy/lookup.cafe/backend-proxy.conf

Proxy Rules

# Socket.IO with WebSocket support
<Location /socket.io>
    ProxyPass http://127.0.0.1:5000/socket.io
    ProxyPassReverse http://127.0.0.1:5000/socket.io
    
    # WebSocket upgrade headers
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*) ws://127.0.0.1:5000/$1 [P,L]
    
    # Forwarded headers
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"
</Location>

# API endpoints
<Location /api>
    ProxyPass http://127.0.0.1:5000/api
    ProxyPassReverse http://127.0.0.1:5000/api
</Location>

# Health check
<Location /health>
    ProxyPass http://127.0.0.1:5000/health
    ProxyPassReverse http://127.0.0.1:5000/health
</Location>

✅ Socket.IO Client Configuration

Connection URL

Production: Uses window.location.origin (automatically HTTPS)

  • When accessed via https://lookup.cafe, connects to wss://lookup.cafe
  • When accessed via http://localhost:5173, connects to http://localhost:5173

Client Code

// src/App.tsx
initializeSocket({
  url: import.meta.env.VITE_BACKEND_URL || window.location.origin,
  reconnection: true,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 5000,
  reconnectionAttempts: 5,
});

Environment Variable

# .env (empty = use same origin)
VITE_BACKEND_URL=

How It Works

Browser (HTTPS)
    ↓
https://lookup.cafe
    ↓
Socket.IO connects to window.location.origin
    ↓
wss://lookup.cafe/socket.io/
    ↓
Apache (Port 443)
    ↓
Detects WebSocket Upgrade header
    ↓
Proxies to ws://127.0.0.1:5000/socket.io/
    ↓
Flask Backend (Port 5000)
    ↓
Socket.IO server handles connection
    ↓
Response flows back through proxy
    ↓
Browser receives via WSS (secure WebSocket)

Connection Protocols

Access Method Socket.IO Protocol Proxied To
https://lookup.cafe wss:// (WebSocket Secure) ws://127.0.0.1:5000
http://localhost:5173 (dev) ws:// (WebSocket) Direct connection

Testing

Test Socket.IO Polling (HTTP fallback)

curl -s "https://lookup.cafe/socket.io/?EIO=4&transport=polling"

Expected: JSON response with session ID

Test API Endpoint

curl -s https://lookup.cafe/api/rooms | jq

Expected: {"rooms": []}

Test Health Check

curl -s https://lookup.cafe/health | jq

Expected: {"status": "ok", "database": "ok", "active_rooms": 0}

Test in Browser Console

// Should see in console:
Socket connected: <socket-id>

Advantages of This Setup

Automatic Protocol - Client automatically uses WSS for HTTPS, WS for HTTP ✅ No Hardcoded URLs - Uses same origin, works on any domain ✅ SSL Termination - Apache handles HTTPS, backend stays HTTP ✅ WebSocket Upgrade - Properly configured for real-time communication
Fallback Support - Socket.IO can use polling if WebSocket fails ✅ Development Friendly - Works on localhost without changes

Troubleshooting

Check WebSocket Connection

Open browser DevTools → Network → WS tab

  • Should see connection to wss://lookup.cafe/socket.io/
  • Status should be "101 Switching Protocols"

Check Backend Logs

tail -f /tmp/lookup-backend.log

Look for Socket.IO connection messages

Check Apache Modules

apachectl -M | grep -E "proxy|rewrite"

Required modules:

  • proxy_module
  • proxy_http_module
  • proxy_wstunnel_module
  • rewrite_module

Force Rebuild Apache Config

/scripts/rebuildhttpdconf && /scripts/restartsrv_httpd

Production Checklist

  • Apache proxy configured with WebSocket support
  • Socket.IO client uses window.location.origin
  • .htaccess excludes proxy paths
  • Backend listening on 0.0.0.0:5000
  • CORS configured for lookup.cafe
  • Frontend rebuilt with new config
  • HTTPS working via Apache
  • WebSocket upgrade headers set

Fix Applied (December 20, 2025)

Issue

WebSocket connections were failing with "WebSocket connection to 'wss://lookup.cafe/socket.io/?EIO=4&transport=websocket' failed" error (400 BAD REQUEST).

Root Cause

The Apache proxy configuration had the RewriteRule inside the <Location> block, which doesn't work properly for WebSocket upgrade requests. The rewrite rules need to be inside a <LocationMatch> block or at the VirtualHost level for proper WebSocket handling.

Solution

Updated /etc/apache2/conf.d/userdata/std/2_4/ishaglcy/lookup.cafe/backend-proxy.conf to use <LocationMatch> with proper RewriteEngine directives that execute before ProxyPass:

<LocationMatch "^/socket\.io">
    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"
    
    # Handle WebSocket upgrades
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/socket\.io/(.*)$ "ws://127.0.0.1:5000/socket.io/$1" [P,L]
    
    # Regular HTTP proxy for polling  
    ProxyPass "http://127.0.0.1:5000/socket.io"
    ProxyPassReverse "http://127.0.0.1:5000/socket.io"
</LocationMatch>

This configuration:

  1. Checks for WebSocket upgrade headers
  2. Rewrites to ws:// protocol when WebSocket upgrade is detected
  3. Falls through to regular HTTP proxy for Socket.IO polling transport
  4. Properly handles the full Socket.IO path including query parameters

After applying this configuration and reloading Apache, WebSocket connections should work correctly.


Final Fix Applied (December 20, 2025 - 06:45 UTC)

Root Cause

The WebSocket connection errors were caused by TWO issues:

  1. The .htaccess file was intercepting Socket.IO requests with a simple [P,L] proxy rule that doesn't handle WebSocket upgrades
  2. The backend was using eventlet async_mode which had compatibility issues with proxied WebSocket connections

Solution

  1. Updated .htaccess to include explicit WebSocket upgrade handling:

    • Added separate RewriteRule for WebSocket upgrades (ws:// protocol)
    • Added separate RewriteRule for regular Socket.IO polling (HTTP)
    • The WebSocket rule checks for Upgrade: websocket and Connection: upgrade headers
  2. Changed backend async_mode from eventlet to None (threading) in config.py:

    • Threading mode has better compatibility with reverse proxy setups
    • More reliable WebSocket upgrade handling

Verified Fix

WebSocket connections now successfully upgrade from HTTP to WebSocket protocol:

HTTP/1.1 101 Switching Protocols
Sec-WebSocket-Accept: ...
Connection: Upgrade
Upgrade: websocket

The application now properly uses WebSocket transport for real-time communication instead of falling back to long-polling.