# WebSocket Separate Domain Setup Guide This guide explains how to configure Laravel Reverb WebSocket server on a separate subdomain (e.g., `ws.yourdomain.org`) instead of using the main application domain. ## Why Use a Separate Domain? Using a separate subdomain for WebSocket connections provides several benefits: - **Better organization**: Clear separation between HTTP and WebSocket traffic - **Easier monitoring**: Dedicated logs and metrics for WebSocket connections - **Flexible scaling**: Can be hosted on different servers or load balancers - **Security isolation**: Separate security policies and firewall rules - **SSL/TLS management**: Independent certificate management ## Prerequisites Before starting, ensure you have: - A working Laravel Reverb installation (see `WEBSOCKET_SETUP.md`) - DNS access to create subdomains - Apache2 with proxy modules enabled - SSL certificate for the WebSocket subdomain (recommended for production) ## Setup Steps ### 1. DNS Configuration Create an A record for your WebSocket subdomain pointing to your server's IP address. **Example DNS Record:** ``` Type: A Name: ws Value: 203.0.113.10 (your server IP) TTL: 3600 ``` This creates `ws.yourdomain.org` pointing to your server. **Verification:** ```bash # Test DNS resolution dig ws.yourdomain.org # Or use nslookup nslookup ws.yourdomain.org ``` Wait for DNS propagation (usually 5-15 minutes, but can take up to 48 hours). ### 2. Apache Virtual Host Configuration Create a dedicated Apache VirtualHost for the WebSocket subdomain. #### Option A: HTTP Only (Development/Testing) Create `/etc/apache2/sites-available/ws-yourdomain.conf`: ```apache ServerName ws.yourdomain.org ServerAdmin webmaster@yourdomain.org # WebSocket proxy for Reverb # Proxy WebSocket connections to /app/* to Reverb server ProxyPass /app/ ws://127.0.0.1:8080/app/ ProxyPassReverse /app/ ws://127.0.0.1:8080/app/ # Regular HTTP proxy for Reverb API endpoints ProxyPass /apps/ http://127.0.0.1:8080/apps/ ProxyPassReverse /apps/ http://127.0.0.1:8080/apps/ # Logging ErrorLog ${APACHE_LOG_DIR}/ws-error.log CustomLog ${APACHE_LOG_DIR}/ws-access.log combined ``` #### Option B: HTTPS (Production - Recommended) Create `/etc/apache2/sites-available/ws-yourdomain-ssl.conf`: ```apache ServerName ws.yourdomain.org # Redirect all HTTP to HTTPS Redirect permanent / https://ws.yourdomain.org/ ServerName ws.yourdomain.org ServerAdmin webmaster@yourdomain.org # SSL Configuration SSLEngine on SSLCertificateFile /etc/letsencrypt/live/ws.yourdomain.org/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/ws.yourdomain.org/privkey.pem # Modern SSL configuration SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite HIGH:!aNULL:!MD5 SSLHonorCipherOrder on # WebSocket proxy for Reverb (wss:// for secure connections) ProxyPass /app/ ws://127.0.0.1:8080/app/ ProxyPassReverse /app/ ws://127.0.0.1:8080/app/ # Regular HTTPS proxy for Reverb API endpoints ProxyPass /apps/ http://127.0.0.1:8080/apps/ ProxyPassReverse /apps/ http://127.0.0.1:8080/apps/ # Logging ErrorLog ${APACHE_LOG_DIR}/ws-ssl-error.log CustomLog ${APACHE_LOG_DIR}/ws-ssl-access.log combined ``` ### 3. Enable Apache Modules and Site ```bash # Enable required Apache modules (if not already enabled) sudo a2enmod proxy sudo a2enmod proxy_http sudo a2enmod proxy_wstunnel sudo a2enmod ssl # For HTTPS # Enable the WebSocket site sudo a2ensite ws-yourdomain.conf # Or for SSL: sudo a2ensite ws-yourdomain-ssl.conf # Test Apache configuration sudo apache2ctl configtest # If test is successful, reload Apache sudo systemctl reload apache2 ``` ### 4. SSL Certificate Setup (Production) For production environments, obtain an SSL certificate using Let's Encrypt: ```bash # Install Certbot sudo apt-get update sudo apt-get install certbot python3-certbot-apache # Obtain SSL certificate for WebSocket subdomain sudo certbot --apache -d ws.yourdomain.org # Certbot will automatically configure Apache for HTTPS # Follow the prompts to complete setup # Test certificate renewal sudo certbot renew --dry-run ``` ### 5. Update Laravel Environment Variables Update your `.env` file to use the separate WebSocket domain: **For HTTP (Development/Testing):** ```env PUSHER_HOST=ws.yourdomain.org PUSHER_PORT=80 PUSHER_SCHEME=http # Mirror for Reverb REVERB_HOST="${PUSHER_HOST}" REVERB_PORT="${PUSHER_PORT}" REVERB_SCHEME="${PUSHER_SCHEME}" # For Vite build-time variables VITE_REVERB_APP_KEY="${PUSHER_APP_KEY}" VITE_REVERB_HOST="${PUSHER_HOST}" VITE_REVERB_PORT="${PUSHER_PORT}" VITE_REVERB_SCHEME="${PUSHER_SCHEME}" ``` **For HTTPS (Production - Recommended):** ```env PUSHER_HOST=ws.yourdomain.org PUSHER_PORT=443 PUSHER_SCHEME=https # Mirror for Reverb REVERB_HOST="${PUSHER_HOST}" REVERB_PORT="${PUSHER_PORT}" REVERB_SCHEME="${PUSHER_SCHEME}" # For Vite build-time variables VITE_REVERB_APP_KEY="${PUSHER_APP_KEY}" VITE_REVERB_HOST="${PUSHER_HOST}" VITE_REVERB_PORT="${PUSHER_PORT}" VITE_REVERB_SCHEME="${PUSHER_SCHEME}" ``` **Important Notes:** - Do NOT use variable references (e.g., `"${PUSHER_HOST}"`) for `PUSHER_HOST`, `PUSHER_APP_KEY`, `PUSHER_APP_SECRET` in production - Hard-code these values as they may be used by external services that don't expand variables - Keep `PUSHER_APP_KEY` and `PUSHER_APP_SECRET` unchanged from your original setup ### 6. Rebuild Frontend Assets After updating environment variables, you must rebuild the frontend assets to include the new WebSocket configuration: ```bash # Clear Laravel caches php artisan config:clear php artisan cache:clear # Rebuild config cache php artisan config:cache # Rebuild frontend assets npm run build ``` ### 7. Restart Services Restart all relevant services to apply the changes: ```bash # Restart Laravel Reverb sudo systemctl restart timebank-reverb.service # Restart queue workers (if using queued broadcasts) php artisan queue:restart # Verify Reverb is running sudo systemctl status timebank-reverb.service # Check that Reverb is listening ss -tlnp | grep 8080 ``` ## Testing the Configuration ### 1. Test DNS Resolution ```bash # Verify DNS is resolving correctly ping ws.yourdomain.org # Check DNS record dig ws.yourdomain.org ``` ### 2. Test Apache Proxy ```bash # Test HTTP connection curl -I http://ws.yourdomain.org/apps/ # Test HTTPS connection (if configured) curl -I https://ws.yourdomain.org/apps/ ``` Expected response: HTTP headers from Reverb server ### 3. Test WebSocket Connection **Using wscat:** ```bash # Install wscat (if not already installed) npm install -g wscat # Test WebSocket connection (HTTP) wscat -c "ws://ws.yourdomain.org/app/your-app-key?protocol=7&client=js&version=7.0.0" # Test secure WebSocket connection (HTTPS) wscat -c "wss://ws.yourdomain.org/app/your-app-key?protocol=7&client=js&version=7.0.0" ``` Successful connection shows: ``` Connected (press CTRL+C to quit) < {"event":"pusher:connection_established","data":"{...}"} ``` ### 4. Test from Browser Console Open your application in a browser and check the console: ```javascript // Check Echo configuration console.log(window.Echo); console.log(window.Echo.connector.pusher.config); // Test connection Echo.connector.pusher.connection.bind('connected', () => { console.log('WebSocket connected to:', Echo.connector.pusher.config.wsHost); }); Echo.connector.pusher.connection.bind('error', (err) => { console.error('WebSocket error:', err); }); ``` ### 5. Check Apache Logs Monitor the WebSocket logs for connection attempts: ```bash # Follow WebSocket access logs sudo tail -f /var/log/apache2/ws-access.log # Follow WebSocket error logs sudo tail -f /var/log/apache2/ws-error.log # Or for SSL: sudo tail -f /var/log/apache2/ws-ssl-access.log sudo tail -f /var/log/apache2/ws-ssl-error.log ``` ## Troubleshooting ### Issue: DNS Not Resolving **Symptoms**: `ping ws.yourdomain.org` fails or returns wrong IP **Solutions**: 1. Wait for DNS propagation (can take up to 48 hours) 2. Check DNS configuration at your DNS provider 3. Flush local DNS cache: `sudo systemd-resolve --flush-caches` 4. Try different DNS server: `dig @8.8.8.8 ws.yourdomain.org` ### Issue: Connection Refused / 502 Bad Gateway **Symptoms**: Browser shows connection error, Apache logs show "Connection refused" **Solutions**: 1. Verify Reverb is running: `sudo systemctl status timebank-reverb.service` 2. Check Reverb is listening on port 8080: `ss -tlnp | grep 8080` 3. Check Apache proxy configuration: `sudo apache2ctl -t -D DUMP_VHOSTS` 4. Verify proxy modules are enabled: `apache2ctl -M | grep proxy` 5. Check firewall rules allow localhost connections: `sudo ufw status` ### Issue: SSL Certificate Errors **Symptoms**: Browser shows SSL certificate warning **Solutions**: 1. Verify certificate is valid: `sudo certbot certificates` 2. Check certificate paths in Apache config match actual certificate location 3. Ensure certificate covers the WebSocket subdomain: `openssl x509 -in /etc/letsencrypt/live/ws.yourdomain.org/cert.pem -text -noout | grep DNS` 4. Reload Apache after certificate renewal: `sudo systemctl reload apache2` ### Issue: WebSocket Connects but Closes Immediately **Symptoms**: Connection establishes but closes within seconds **Solutions**: 1. Check Reverb logs: `sudo journalctl -u timebank-reverb.service -f` 2. Verify `PUSHER_APP_KEY` matches in `.env` and frontend 3. Check Redis is running: `redis-cli ping` 4. Verify all environment variables are correct and frontend was rebuilt 5. Clear browser cache and hard refresh (Ctrl+Shift+R) ### Issue: Mixed Content Errors (HTTP/HTTPS) **Symptoms**: Console shows "Mixed Content" errors when using HTTPS site with HTTP WebSocket **Solutions**: 1. Ensure WebSocket uses same scheme as main site (both HTTP or both HTTPS) 2. Update `.env` to use `PUSHER_SCHEME=https` and `PUSHER_PORT=443` 3. Configure SSL for WebSocket subdomain (see SSL Certificate Setup above) 4. Rebuild frontend assets: `npm run build` ### Issue: Assets Not Updated After .env Changes **Symptoms**: WebSocket still trying to connect to old domain **Solutions**: ```bash # Clear all caches php artisan config:clear php artisan cache:clear php artisan view:clear # Rebuild config cache php artisan config:cache # Rebuild frontend assets npm run build # Hard refresh browser (Ctrl+Shift+R) ``` ## Security Considerations ### Firewall Configuration Ensure your firewall allows traffic to the WebSocket subdomain: ```bash # Allow HTTP (port 80) sudo ufw allow 80/tcp # Allow HTTPS (port 443) sudo ufw allow 443/tcp # Block direct access to Reverb from external networks # Only allow localhost connections sudo ufw deny 8080/tcp sudo ufw allow from 127.0.0.1 to any port 8080 ``` ### Additional Security Measures 1. **Use HTTPS in production**: Always use `wss://` (WebSocket over SSL) for production 2. **Strong credentials**: Use secure random keys for `PUSHER_APP_KEY` and `PUSHER_APP_SECRET` 3. **Rate limiting**: Configure rate limiting in Apache or at application level 4. **CORS configuration**: Update `allowed_origins` in `config/reverb.php` if needed 5. **Monitor logs**: Regularly check WebSocket logs for suspicious activity ### Content Security Policy (CSP) Update your Content Security Policy headers to allow WebSocket connections to the subdomain: ```apache # Add to your main application VirtualHost Header set Content-Security-Policy "connect-src 'self' ws://ws.yourdomain.org wss://ws.yourdomain.org;" ``` ## Maintenance ### Monitoring WebSocket Connections ```bash # Count active WebSocket connections through Apache sudo tail -f /var/log/apache2/ws-ssl-access.log | grep "GET /app/" # Monitor Reverb process watch -n 1 'ps aux | grep reverb' # Monitor port usage watch -n 1 'ss -t | grep :8080 | wc -l' ``` ### Log Rotation Ensure log rotation is configured for WebSocket logs: Create `/etc/logrotate.d/websocket-apache`: ``` /var/log/apache2/ws-*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 www-data adm sharedscripts postrotate if invoke-rc.d apache2 status > /dev/null 2>&1; then \ invoke-rc.d apache2 reload > /dev/null 2>&1; \ fi; endscript } ``` ## Quick Reference ### Essential Commands ```bash # DNS testing dig ws.yourdomain.org nslookup ws.yourdomain.org # Apache configuration sudo apache2ctl configtest sudo systemctl reload apache2 sudo a2ensite ws-yourdomain-ssl.conf # SSL certificate sudo certbot --apache -d ws.yourdomain.org sudo certbot renew --dry-run # Service management sudo systemctl restart timebank-reverb.service sudo systemctl status timebank-reverb.service # Testing WebSocket wscat -c "wss://ws.yourdomain.org/app/your-app-key?protocol=7&client=js&version=7.0.0" # Logs sudo tail -f /var/log/apache2/ws-ssl-access.log sudo tail -f /var/log/apache2/ws-ssl-error.log sudo journalctl -u timebank-reverb.service -f ``` ### Configuration Files - **DNS**: Your DNS provider's control panel - **Apache VirtualHost**: `/etc/apache2/sites-available/ws-yourdomain-ssl.conf` - **SSL Certificate**: `/etc/letsencrypt/live/ws.yourdomain.org/` - **Laravel .env**: `/path/to/your/app/.env` - **Frontend config**: Embedded in built assets via Vite environment variables ## Additional Resources - **Main WebSocket Setup**: `WEBSOCKET_SETUP.md` - **Laravel Reverb Documentation**: https://laravel.com/docs/11.x/reverb - **Apache Proxy Guide**: https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html - **Let's Encrypt**: https://letsencrypt.org/getting-started/