La proxyficación de conexiones WebSocket implica configurar un servidor intermediario para manejar correctamente el handshake HTTP Upgrade y mantener la conexión TCP persistente y full-duplex requerida para la comunicación WebSocket. Esta configuración permite características como el balanceo de carga, la terminación SSL, el almacenamiento en caché y el control de acceso para aplicaciones en tiempo real.
Comprensión de la Proxyficación de WebSocket
Los WebSockets establecen una conexión persistente entre un cliente y un servidor sobre una única conexión TCP. Esto difiere del HTTP tradicional, que típicamente utiliza ciclos de solicitud/respuesta de corta duración. La transición de HTTP a WebSocket se inicia a través de un mecanismo de "Upgrade" en el protocolo HTTP/1.1.
El cliente envía una solicitud HTTP GET inicial con encabezados específicos:
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: [nonce codificado en base64]
* Sec-WebSocket-Version: 13
Si el servidor soporta WebSockets, responde con un estado HTTP 101 Switching Protocols, confirmando la actualización:
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: [clave de respuesta derivada de la clave del cliente]
Después de este handshake, la conexión ya no es HTTP sino un socket TCP puro, sobre el cual se intercambian frames de WebSocket. Un servidor proxy debe configurarse para reenviar correctamente estos encabezados Upgrade y Connection y luego mantener la conexión TCP de larga duración sin almacenar en búfer ni cerrarla prematuramente.
Configuración para Servidores Proxy Comunes
Nginx
Nginx puede proxyficar conexiones WebSocket de manera efectiva manejando el handshake HTTP Upgrade. La clave es pasar los encabezados Upgrade y Connection del cliente al servidor backend y asegurar que se use HTTP/1.1.
http {
upstream websocket_backend {
server backend_server_1:8080;
server backend_server_2:8080;
}
server {
listen 80;
server_name yourdomain.com;
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host; # Importante para el backend si depende del encabezado host
proxy_read_timeout 86400s; # Ajustar según sea necesario para conexiones de larga duración
proxy_send_timeout 86400s;
proxy_buffering off; # Deshabilitar el almacenamiento en búfer para datos en tiempo real
}
# Otras ubicaciones HTTP
location / {
proxy_pass http://other_http_backend;
# ... otras configuraciones de proxy HTTP
}
}
}
proxy_http_version 1.1;: Esencial para el mecanismo del encabezadoUpgrade.proxy_set_header Upgrade $http_upgrade;: Reenvía el encabezadoUpgradedel cliente.proxy_set_header Connection "upgrade";: Reenvía el encabezadoConnectiondel cliente. Nginx maneja automáticamente la transformación deConnection: upgradeaConnection: keep-alivepara conexiones HTTP/1.1 si no se establece explícitamente a "upgrade". Establecerlo explícitamente asegura un comportamiento correcto para WebSockets.proxy_read_timeout/proxy_send_timeout: Aumentar estos valores predeterminados para evitar que Nginx cierre conexiones WebSocket de larga duración debido a la inactividad.proxy_buffering off;: Evita que Nginx almacene en búfer las respuestas, lo cual es crítico para la comunicación WebSocket en tiempo real para asegurar una latencia mínima.
Servidor HTTP Apache
Apache puede proxyficar conexiones WebSocket usando mod_proxy_wstunnel, que forma parte de mod_proxy. Asegúrese de que mod_proxy, mod_proxy_http y mod_proxy_wstunnel estén habilitados.
<VirtualHost *:80>
ServerName yourdomain.com
# Habilitar módulos proxy
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
# Proxyficar conexiones WebSocket
<Location /ws/>
ProxyPass ws://backend_server:8080/ws/
ProxyPassReverse ws://backend_server:8080/ws/
</Location>
# Proxyficar conexiones WebSocket seguras (WSS)
<Location /wss/>
ProxyPass wss://backend_server:8443/wss/
ProxyPassReverse wss://backend_server:8443/wss/
</Location>
# Otras ubicaciones HTTP
<Location />
ProxyPass http://other_http_backend/
ProxyPassReverse http://other_http_backend/
</Location>
</VirtualHost>
ProxyPass ws://...: El esquemaws://le indica amod_proxy_wstunnelque maneje la actualización de WebSocket. Para WebSockets seguros, usewss://.ProxyPassReverse: Asegura la reescritura correcta de URL en los encabezados de respuesta.mod_proxyde Apache maneja automáticamente los encabezadosUpgradeyConnectioncuando se especificaws://owss://.
HAProxy
HAProxy soporta la proxyficación de WebSocket desde la versión 1.5. Puede operar en modo http para inspeccionar encabezados o en modo tcp para el reenvío puro. Para WebSockets, es común usar el modo http con reglas específicas para detectar la solicitud de actualización.
global
log /dev/log local0 info
maxconn 4000
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms # Aumentado para conexiones WebSocket del lado del cliente
timeout server 50000ms # Aumentado para conexiones WebSocket del lado del backend
frontend http_frontend
bind *:80
acl is_websocket hdr(Upgrade) -i websocket
use_backend ws_backend if is_websocket
default_backend http_backend
backend ws_backend
mode http
# Esta opción es crucial para WebSockets en modo HTTP; le indica a HAProxy que
# cambie a tunelización TCP después del handshake HTTP.
option http-tunnel
server backend_ws_1 backend_server_1:8080 check
server backend_ws_2 backend_server_2:8080 check
backend http_backend
mode http
server backend_http_1 backend_server_3:80 check
acl is_websocket hdr(Upgrade) -i websocket: Define una Lista de Control de Acceso (ACL) que coincide con las solicitudes que contienen un encabezadoUpgrade: websocket(sin distinción entre mayúsculas y minúsculas).use_backend ws_backend if is_websocket: Dirige las solicitudes de actualización de WebSocket alws_backend.option http-tunnel: Esto es crítico. Cuando HAProxy detecta un encabezadoUpgradey esta opción está configurada, cambia del modo de análisis HTTP a un túnel TCP puro después del handshake HTTP inicial. Esto permite que el protocolo WebSocket opere sin impedimentos.timeout client/timeout server: Estos tiempos de espera deben aumentarse significativamente para los backends de WebSocket para evitar que HAProxy cierre conexiones de larga duración.
Solución de Problemas Comunes
Caída de Conexión o Tiempo de Espera Agotado
- Tiempos de espera del Proxy: La causa más común. Asegúrese de que
proxy_read_timeout,proxy_send_timeout(Nginx),timeout client,timeout server(HAProxy), o configuraciones equivalentes en otros proxies sean suficientemente altos (por ejemplo, 24 horas o más) para las conexiones WebSocket. - Conexiones Inactivas: Algunos proxies o dispositivos de red pueden cerrar agresivamente las conexiones TCP inactivas. Si el protocolo WebSocket tiene un mecanismo de keep-alive (por ejemplo, frames ping/pong), asegúrese de que esté configurado tanto en el cliente como en el servidor para enviar pings periódicos.
- Inactividad del Balanceador de Carga: Si un balanceador de carga se encuentra delante del proxy, también podría tener sus propios tiempos de espera por inactividad. Revise estas configuraciones.
HTTP 400 Bad Request (Encabezado Upgrade Faltante o Incorrecto)
- Solicitud del Cliente: Verifique que el cliente esté enviando correctamente los encabezados
Upgrade: websocketyConnection: Upgrade. - Configuración del Proxy: Asegúrese de que el proxy esté configurado para reenviar estos encabezados al backend.
- Nginx: Verifique
proxy_set_header Upgrade $http_upgrade;yproxy_set_header Connection "upgrade";. - Apache: Asegúrese de que
mod_proxy_wstunnelesté habilitado yProxyPassusews://owss://. - HAProxy: Confirme que
acl is_websocketidentifique correctamente el encabezadoUpgradey queuse_backendlo dirija a un backend conoption http-tunnel.
- Nginx: Verifique
HTTP 502 Bad Gateway
- Problemas del Servidor Backend: El proxy recibió una respuesta inválida del backend.
- ¿Está el servidor WebSocket backend en ejecución y escuchando en el puerto esperado?
- ¿Está el backend configurado correctamente para manejar las actualizaciones de WebSocket?
- Revise los registros del servidor backend en busca de errores.
- Conectividad de Red: Verifique que el proxy pueda alcanzar el servidor backend en el puerto especificado.
- Almacenamiento en Búfer del Proxy: Si bien
proxy_buffering offes generalmente bueno para WebSockets, los datos excesivos o las configuraciones incorrectas a veces pueden provocar 502 si el proxy tiene dificultades para manejar el flujo.
Consideraciones de Seguridad (Terminación SSL/TLS)
- WSS (WebSockets Seguros): Para conexiones WebSocket seguras (wss://), el proxy debe manejar la terminación SSL/TLS.
- El proxy escuchará en el puerto 443 (u otro puerto seguro), realizará el handshake TLS y luego proxyficará el tráfico WebSocket descifrado al backend (a menudo a través de
ws://ohttp://internamente). - Ejemplo de Nginx para WSS:
```nginx
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/yourdomain.crt;
ssl_certificate_key /etc/nginx/ssl/yourdomain.key;location /ws/ { proxy_pass http://websocket_backend; # El backend podría ser HTTP plano proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 86400s; proxy_send_timeout 86400s; proxy_buffering off; }}
`` * **Encabezados de Origen:** Los clientes WebSocket envían un encabezadoOrigin`. Los servidores backend a menudo validan este encabezado por razones de seguridad. Asegúrese de que su proxy no esté eliminando o alterando este encabezado si el backend depende de él.
- El proxy escuchará en el puerto 443 (u otro puerto seguro), realizará el handshake TLS y luego proxyficará el tráfico WebSocket descifrado al backend (a menudo a través de
Límites del Búfer del Proxy
- Nginx:
proxy_buffering off;es crucial. Si no se establece, Nginx podría almacenar en búfer mensajes WebSocket grandes, introduciendo latencia o causando tiempos de espera. - HAProxy:
option http-tunnelmaneja esto cambiando a TCP puro.
Comparación de Servidores Proxy para WebSockets
| Característica | Nginx | Servidor HTTP Apache (mod_proxy_wstunnel) | HAProxy |
|---|---|---|---|
| Configuración | Reenvío manual de encabezados | Directivas específicas ws:// / wss:// |
ACLs y option http-tunnel |
| Complejidad | Moderada | Moderada | Moderada a Alta (para características avanzadas) |
| Rendimiento | Alto (Orientado a eventos) | Moderado (Basado en procesos/hilos) | Alto (Orientado a eventos) |
| Terminación SSL/TLS | Excelente | Bueno | Excelente |
| Balanceo de Carga | Round-robin, hash IP, menos conexiones | Round-robin | Extenso (muchos algoritmos, comprobaciones de salud) |
| Sesiones Pegajosas | Hash IP (para básico), módulos de terceros | Limitado | Basado en cookies, IP de origen |
| Control de Búfer | proxy_buffering off |
Manejado por mod_proxy_wstunnel |
option http-tunnel deshabilita el búfer HTTP |
Consideraciones Clave
- Sesiones Pegajosas: Para algunas aplicaciones WebSocket, un cliente podría necesitar permanecer conectado al mismo servidor backend durante toda su sesión (por ejemplo, si se mantiene el estado del lado del servidor).
- Nginx: Use
ip_hashen el bloqueupstream. - HAProxy: Use las directivas
balance sourceocookie. - Asegúrese de que su aplicación backend esté diseñada para la escalabilidad horizontal sin requerir sesiones pegajosas si es posible, ya que simplifica el balanceo de carga.
- Nginx: Use
- Comprobaciones de Salud: Configure comprobaciones de salud robustas para sus servidores WebSocket backend dentro del proxy. Esto asegura que solo los servidores saludables reciban solicitudes de actualización de WebSocket.
- Registro (Logging): Configure un registro completo en el proxy y el backend. Los problemas de conexión WebSocket pueden ser transitorios, y los registros detallados son esenciales para el diagnóstico.
- HTTP/2 y WebSockets: Si bien los WebSockets típicamente usan HTTP/1.1 para el handshake de actualización, pueden coexistir con HTTP/2. Sin embargo, el protocolo WebSocket en sí no se ejecuta directamente sobre frames HTTP/2. Los proxies que manejan HTTP/2 de los clientes generalmente degradarán a HTTP/1.1 para la actualización de WebSocket al backend.