# WebSocket Keep-Alive Configuration Guide ## Quick Fix for SNI Passthrough WebSocket Disconnections If your WebSocket connections are disconnecting every 30 seconds in SNI passthrough mode, here's the immediate solution: ### Option 1: Extended Keep-Alive Treatment (Recommended) ```typescript const proxy = new SmartProxy({ // Extend timeout for keep-alive connections keepAliveTreatment: 'extended', keepAliveInactivityMultiplier: 10, // 10x the base timeout inactivityTimeout: 14400000, // 4 hours base (40 hours with multiplier) routes: [ { name: 'websocket-passthrough', match: { ports: 443, domains: ['ws.example.com', 'wss.example.com'] }, action: { type: 'forward', target: { host: 'backend', port: 443 }, tls: { mode: 'passthrough' } } } ] }); ``` ### Option 2: Immortal Connections (Never Timeout) ```typescript const proxy = new SmartProxy({ // Never timeout keep-alive connections keepAliveTreatment: 'immortal', routes: [ // ... same as above ] }); ``` ### Option 3: Per-Route Security Settings ```typescript const proxy = new SmartProxy({ routes: [ { name: 'websocket-passthrough', match: { ports: 443, domains: ['ws.example.com'] }, action: { type: 'forward', target: { host: 'backend', port: 443 }, tls: { mode: 'passthrough' } }, security: { // Disable connection limits for this route maxConnections: 0, // 0 = unlimited maxConnectionsPerIP: 0 // 0 = unlimited } } ] }); ``` ## Understanding the Issue ### Why Connections Drop at 30 Seconds 1. **WebSocket Heartbeat**: The HTTP proxy's WebSocket handler sends ping frames every 30 seconds 2. **SNI Passthrough**: In passthrough mode, traffic is encrypted end-to-end 3. **Can't Inject Pings**: The proxy can't inject ping frames into encrypted traffic 4. **No Pong Response**: Client doesn't respond to pings that were never sent 5. **Connection Terminated**: After 30 seconds, connection is marked inactive and closed ### Why Grace Periods Were Too Short - Half-zombie detection: 30 seconds (now 5 minutes for TLS) - Stuck connection detection: 60 seconds (now 5 minutes for TLS) - These were too aggressive for encrypted long-lived connections ## Long-Term Solution The fix involves: 1. **Detecting SNI Passthrough**: Skip WebSocket heartbeat for passthrough connections 2. **Longer Grace Periods**: 5-minute grace for encrypted connections 3. **TCP Keep-Alive**: Rely on OS-level TCP keep-alive instead 4. **Route-Aware Timeouts**: Different timeout strategies per route type ## TCP Keep-Alive Configuration For best results, also configure TCP keep-alive at the OS level: ### Linux ```bash # /etc/sysctl.conf net.ipv4.tcp_keepalive_time = 600 # Start probes after 10 minutes net.ipv4.tcp_keepalive_intvl = 60 # Probe every minute net.ipv4.tcp_keepalive_probes = 9 # Drop after 9 failed probes ``` ### Node.js Socket Options The proxy already enables TCP keep-alive on sockets: - Keep-alive is enabled by default - Initial delay can be configured via `keepAliveInitialDelay` ## Monitoring Check your connections: ```typescript const stats = proxy.getStats(); console.log('Active connections:', stats.getActiveConnections()); console.log('Connections by route:', stats.getConnectionsByRoute()); // Monitor long-lived connections setInterval(() => { const connections = proxy.connectionManager.getConnections(); for (const [id, conn] of connections) { const age = Date.now() - conn.incomingStartTime; if (age > 300000) { // 5+ minutes console.log(`Long-lived connection: ${id}, age: ${age}ms, route: ${conn.routeName}`); } } }, 60000); ``` ## Summary - **Immediate Fix**: Use `keepAliveTreatment: 'extended'` or `'immortal'` - **Applied Fix**: Increased grace periods for TLS connections to 5 minutes - **Best Practice**: Use SNI passthrough for WebSocket when you need end-to-end encryption - **Alternative**: Use TLS termination if you need application-level WebSocket features