WebSocket no es una solicitud HTTP normal. Después del "apretón de manos" inicial, la conexión permanece abierta y los datos fluyen en ambas direcciones de manera continua. Por esta razón, los proxies HTTP estándar a menudo interrumpen las conexiones WS o no pueden manejarlas en absoluto. En este artículo, analizaremos cómo proxificar correctamente el tráfico de WebSocket: qué proxies son adecuados, cómo configurarlos y qué errores son los más comunes.
Cómo funciona WebSocket y por qué es complicado para los proxies
Para entender el problema, es necesario comprender la mecánica. La conexión WebSocket comienza como una solicitud HTTP normal: el cliente envía el encabezado Upgrade: websocket y Connection: Upgrade. El servidor responde con el código 101 Switching Protocols — y a partir de ese momento, la conexión deja de ser HTTP. Se convierte en un canal bidireccional permanente, a través del cual los datos fluyen en tramas.
Un proxy HTTP/1.1 estándar, que solo puede reenviar solicitudes y respuestas, se enfrenta a un problema: no sabe qué hacer con la conexión después de la respuesta 101. Muchos servidores proxy simplemente cierran la conexión en ese momento o devuelven un error 502 Bad Gateway. Otros mantienen la conexión, pero no pueden transmitir correctamente las tramas de WebSocket, lo que lleva a interrupciones o distorsiones de datos.
Aquí están las diferencias clave entre WebSocket y HTTP desde la perspectiva de la proxificación:
| Parámetro | HTTP | WebSocket |
|---|---|---|
| Tipo de conexión | Solicitud → Respuesta → Cierre | Permanente, bidireccional |
| Tiempo de vida | Segundos (una solicitud) | Minutos, horas, días |
| Iniciador de datos | Solo cliente | Cliente y servidor |
| Protocolo después del handshake | HTTP | Propietario (RFC 6455) |
| Puertos | 80, 443 | 80 (WS), 443 (WSS) |
Debido al cambio de protocolo después del handshake, la mayoría de las soluciones proxy simples no manejan WebSocket. Se necesita usar un proxy que explícitamente soporte tunelización o usar SOCKS5, un protocolo que opera a un nivel más bajo y no analiza el contenido del tráfico.
Qué tipos de proxies soportan WebSocket
No todos los proxies son igualmente útiles para WebSocket. Analicemos cada tipo:
| Tipo de proxy | Soporte WS | Mecanismo | Complejidad |
|---|---|---|---|
| Proxy HTTP | ⚠️ Parcialmente | A través de túnel CONNECT | Media |
| Proxy HTTPS | ✅ Sí | CONNECT + TLS | Media |
| SOCKS4 | ⚠️ Limitado | Túnel TCP sin auth | Baja |
| SOCKS5 | ✅ Completo | TCP/UDP transparente | Baja |
| Proxy transparente | ❌ No | Solo HTTP | — |
Conclusión: para tareas de WebSocket, la mejor opción es SOCKS5. Este protocolo opera a nivel de transporte y simplemente tuneliza la conexión TCP, sin preocuparse por el contenido del tráfico. No le importa si dentro va HTTP, WebSocket, SSH o cualquier otra cosa. Los proxies HTTP también pueden trabajar con WS, pero solo a través del método CONNECT — y aquí hay matices que analizaremos a continuación.
Método CONNECT: cómo el proxy HTTP tuneliza WebSocket
El método HTTP CONNECT es un mecanismo especial que permite a los proxies HTTP crear un túnel "ciego" hacia el servidor de destino. El proxy no analiza el tráfico dentro del túnel, simplemente reenvía los bytes. Así es como funciona HTTPS a través de un proxy HTTP — y así es como se puede proxificar WebSocket.
El proceso se ve así:
- El cliente envía al proxy la solicitud:
CONNECT example.com:443 HTTP/1.1 - El proxy establece una conexión TCP con
example.com:443 - El proxy responde al cliente:
200 Connection Established - A partir de este momento, el proxy simplemente reenvía los bytes de ida y vuelta — sin analizarlos
- El cliente realiza el handshake TLS directamente con el servidor a través del túnel
- Luego, el handshake de WebSocket sobre TLS
Limitación clave: el método CONNECT generalmente está permitido solo para el puerto 443. Si su servidor WebSocket está funcionando en un puerto no estándar (por ejemplo, 8080 o 9000), el proxy puede rechazar la conexión. En ese caso, SOCKS5 es preferible — no tiene restricciones de puertos.
También es importante tener en cuenta que algunos proxies HTTP corporativos (por ejemplo, Squid en configuración estándar) bloquean explícitamente el método CONNECT para ciertos puertos o requieren autenticación. Si trabaja con proveedores de proxies comerciales, la mayoría de ellos soportan CONNECT sin restricciones.
# Ejemplo de solicitud CONNECT a través de curl (para verificar)
curl -v -x http://proxy_host:proxy_port \
--proxytunnel \
https://echo.websocket.org
# Si el proxy soporta CONNECT — verá:
# * CONNECT tunnel established, response 200
SOCKS5 y WebSocket: por qué es la mejor opción
SOCKS5 es un protocolo de proxy a nivel de conexiones TCP/UDP. A diferencia de los proxies HTTP, SOCKS5 no sabe nada sobre el protocolo de aplicación que va dentro. Simplemente crea un túnel entre el cliente y el servidor de destino, y eso es todo. Esto lo hace ideal para WebSocket por varias razones:
- Sin restricciones de protocolo: SOCKS5 tuneliza cualquier tráfico TCP, incluyendo WS, WSS, SSH, FTP, etc.
- Sin restricciones de puertos: funciona con cualquier puerto, no solo 443 o 80
- Sin interrupción al cambiar de protocolo: el proxy no "ve" la transición de HTTP a WebSocket
- Soporte de autenticación: SOCKS5 soporta nombre de usuario/contraseña, lo que es conveniente para proxies comerciales
- Soporte de UDP: si su aplicación utiliza WebRTC o UDP junto con WS — SOCKS5 lo manejará
Prácticamente todas las bibliotecas modernas para trabajar con WebSocket soportan SOCKS5 ya sea directamente o a través de paquetes adicionales. A continuación, veremos ejemplos específicos para Python y Node.js.
💡 ¿Cuándo elegir SOCKS5 y cuándo HTTP CONNECT?
Use SOCKS5 si: puerto no estándar, necesita soporte de UDP, quiere la mínima configuración.
Use HTTP CONNECT si: el proveedor de proxy no soporta SOCKS5, o está trabajando a través de un proxy corporativo.
Ejemplos de código: WebSocket a través de proxy en Python
Veamos algunos escenarios para Python. Las bibliotecas más populares para WebSocket en Python son websockets y websocket-client.
Opción 1: websocket-client a través de proxy HTTP
import websocket
# Configuración del proxy HTTP
proxy_host = "proxy.example.com"
proxy_port = 8080
proxy_user = "username"
proxy_pass = "password"
ws = websocket.WebSocket()
ws.connect(
"wss://echo.websocket.org",
http_proxy_host=proxy_host,
http_proxy_port=proxy_port,
http_proxy_auth=(proxy_user, proxy_pass),
proxy_type="http" # o "socks5"
)
ws.send("¡Hola, WebSocket!")
result = ws.recv()
print(f"Recibido: {result}")
ws.close()
Opción 2: websocket-client a través de SOCKS5
import websocket
# Para SOCKS5 se necesita el paquete: pip install PySocks
ws = websocket.WebSocket()
ws.connect(
"wss://echo.websocket.org",
http_proxy_host="socks5_proxy.example.com",
http_proxy_port=1080,
http_proxy_auth=("username", "password"),
proxy_type="socks5"
)
ws.send("Mensaje de prueba")
print(ws.recv())
ws.close()
Opción 3: biblioteca websockets (asyncio) a través de SOCKS5
La biblioteca websockets (asyncio) no tiene soporte integrado para proxies, por lo que utilizamos python-socks para crear el túnel:
# pip install websockets python-socks[asyncio]
import asyncio
import websockets
from python_socks.async_.asyncio import Proxy
async def connect_via_socks5():
proxy = Proxy.from_url("socks5://username:[email protected]:1080")
# Creamos una conexión TCP a través del proxy
sock = await proxy.connect(
dest_host="echo.websocket.org",
dest_port=443
)
# Pasamos el socket a websockets
async with websockets.connect(
"wss://echo.websocket.org",
sock=sock
) as ws:
await ws.send("¡Hola a través de SOCKS5!")
response = await ws.recv()
print(f"Respuesta: {response}")
asyncio.run(connect_via_socks5())
Opción 4: parche global a través de PySocks
Si desea dirigir todo el tráfico de la aplicación Python a través de SOCKS5 sin modificar cada llamada — use socks.setdefaultproxy():
# pip install PySocks
import socks
import socket
import websocket
# Parcheamos el socket globalmente
socks.set_default_proxy(
socks.SOCKS5,
"proxy.example.com",
1080,
username="user",
password="pass"
)
socket.socket = socks.socksocket
# Ahora todas las conexiones van a través de SOCKS5
ws = websocket.WebSocket()
ws.connect("wss://echo.websocket.org")
ws.send("¡Proxy SOCKS5 global!")
print(ws.recv())
ws.close()
Ejemplos de código: WebSocket a través de proxy en Node.js
En el ecosistema de Node.js, la biblioteca más popular para WebSocket es ws. Para la proxificación a través de HTTP/SOCKS5 se utiliza el paquete https-proxy-agent o socks-proxy-agent.
Opción 1: WSS a través de proxy HTTP CONNECT
// npm install ws https-proxy-agent
const WebSocket = require('ws');
const { HttpsProxyAgent } = require('https-proxy-agent');
const proxyUrl = 'http://username:[email protected]:8080';
const agent = new HttpsProxyAgent(proxyUrl);
const ws = new WebSocket('wss://echo.websocket.org', { agent });
ws.on('open', () => {
console.log('Conexión establecida a través del proxy HTTP');
ws.send('¡Hola desde Node.js!');
});
ws.on('message', (data) => {
console.log(`Recibido: ${data}`);
ws.close();
});
ws.on('error', (err) => {
console.error('Error:', err.message);
});
Opción 2: WSS a través de SOCKS5
// npm install ws socks-proxy-agent
const WebSocket = require('ws');
const { SocksProxyAgent } = require('socks-proxy-agent');
const proxyUrl = 'socks5://username:[email protected]:1080';
const agent = new SocksProxyAgent(proxyUrl);
const ws = new WebSocket('wss://echo.websocket.org', { agent });
ws.on('open', () => {
console.log('Conexión establecida a través de SOCKS5');
ws.send(JSON.stringify({ type: 'ping', data: 'test' }));
});
ws.on('message', (data) => {
console.log('Respuesta:', data.toString());
});
ws.on('close', (code, reason) => {
console.log(`Cerrado: ${code} - ${reason}`);
});
Opción 3: WS (sin TLS) a través de proxy HTTP manualmente
Para WS no cifrado (puerto 80) a través de un proxy HTTP, es necesario enviar manualmente la solicitud CONNECT, ya que los agentes estándar a menudo solo funcionan con HTTPS:
const net = require('net');
const WebSocket = require('ws');
function createTunnel(proxyHost, proxyPort, targetHost, targetPort) {
return new Promise((resolve, reject) => {
const socket = net.connect(proxyPort, proxyHost, () => {
const connectReq =
`CONNECT ${targetHost}:${targetPort} HTTP/1.1\r\n` +
`Host: ${targetHost}:${targetPort}\r\n` +
`Proxy-Authorization: Basic ${Buffer.from('user:pass').toString('base64')}\r\n` +
`\r\n`;
socket.write(connectReq);
});
socket.once('data', (data) => {
if (data.toString().includes('200')) {
resolve(socket);
} else {
reject(new Error(`Proxy rechazó CONNECT: ${data.toString()}`));
}
});
socket.on('error', reject);
});
}
async function main() {
const socket = await createTunnel(
'proxy.example.com', 8080,
'echo.websocket.org', 80
);
const ws = new WebSocket('ws://echo.websocket.org', { socket });
ws.on('open', () => {
ws.send('¡Túnel CONNECT manual!');
});
ws.on('message', (data) => {
console.log('Recibido:', data.toString());
ws.close();
});
}
main().catch(console.error);
WSS (WebSocket Secure): características de la proxificación con TLS
WSS es WebSocket sobre TLS (similar a HTTPS para HTTP). Al proxificar WSS a través de SOCKS5 o HTTP CONNECT, hay un matiz importante: el cifrado TLS se establece entre el cliente y el servidor final, no entre el cliente y el proxy. Esto significa:
- El servidor proxy no ve el contenido del tráfico WSS — solo la dirección IP y el puerto de destino
- El certificado del servidor es verificado directamente por el cliente
- El proxy no puede "suplantar" o "interceptar" datos sin instalar su propio certificado CA
Esta es una buena noticia desde el punto de vista de la seguridad. Pero hay matices prácticos al configurarlo:
Verificación del certificado a través del proxy
A veces, al usar proxies corporativos (que realizan inspección SSL), puede recibir un error de verificación de certificado. En este caso, el proxy reemplaza el certificado del servidor por el suyo. Para trabajar en tal entorno, debe agregar el certificado CA del proxy a los confiables:
# Python: pasamos el certificado CA del proxy corporativo
import ssl
import websocket
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations("/path/to/corporate-ca.crt")
ws = websocket.WebSocket(sslopt={"context": ssl_context})
ws.connect(
"wss://internal.example.com",
http_proxy_host="corp-proxy.company.com",
http_proxy_port=8080,
proxy_type="http"
)
# En entornos de prueba (NO para producción) se puede desactivar la verificación:
ws_test = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE})
ws_test.connect("wss://test.example.com", http_proxy_host="proxy", http_proxy_port=8080)
⚠️ Importante
Desactivar la verificación del certificado TLS (CERT_NONE) es aceptable solo en un entorno de prueba. En producción, esto crea una vulnerabilidad a ataques de tipo MITM (man-in-the-middle).
SNI (Server Name Indication) a través del proxy
Al usar SOCKS5, el cliente puede resolver DNS por sí mismo o delegar esto al proxy. El modo socks5h (SOCKS5 con resolución de nombre de host) significa que la consulta DNS se realiza en el lado del servidor proxy. Esto es importante para WSS, ya que el encabezado SNI en el handshake TLS debe coincidir con el nombre del host:
# socks5 — DNS se resuelve localmente (por el cliente)
# socks5h — DNS se resuelve por el servidor proxy (recomendado para anonimato)
from python_socks.async_.asyncio import Proxy
# DNS a través del proxy (recomendado):
proxy = Proxy.from_url("socks5h://user:[email protected]:1080")
# DNS localmente:
proxy = Proxy.from_url("socks5://user:[email protected]:1080")
Errores comunes y cómo solucionarlos
Hemos recopilado los problemas más frecuentes al proxificar WebSocket con soluciones:
Error 1: 407 Proxy Authentication Required
El proxy requiere autenticación, pero no ha proporcionado las credenciales o las ha proporcionado incorrectamente.
# ❌ Incorrecto — sin autorización
ws.connect("wss://example.com", http_proxy_host="proxy.example.com", http_proxy_port=8080)
# ✅ Correcto — proporcionamos nombre de usuario y contraseña
ws.connect(
"wss://example.com",
http_proxy_host="proxy.example.com",
http_proxy_port=8080,
http_proxy_auth=("username", "password")
)
Error 2: Connection reset by peer / 502 Bad Gateway
El proxy no soporta WebSocket o el método CONNECT. Solución: cambie a SOCKS5 o verifique si su proveedor soporta tráfico WebSocket.
Error 3: La conexión se interrumpe después de 30-60 segundos
Muchos servidores proxy cierran las conexiones TCP "inactivas" por tiempo de espera. La conexión WebSocket puede parecer inactiva si no hay intercambio de datos. La solución es habilitar ping/pong:
# Python — habilitamos el ping keepalive cada 20 segundos
import websocket
import threading
def run():
ws = websocket.WebSocketApp(
"wss://echo.websocket.org",
on_message=lambda ws, msg: print(msg),
on_error=lambda ws, err: print(f"Error: {err}"),
on_close=lambda ws, c, m: print("Cerrado")
)
ws.run_forever(
ping_interval=20, # enviar ping cada 20 seg
ping_timeout=10, # esperar pong no más de 10 seg
http_proxy_host="proxy.example.com",
http_proxy_port=8080,
proxy_type="socks5"
)
thread = threading.Thread(target=run)
thread.start()
Error 4: SSL: CERTIFICATE_VERIFY_FAILED
Ocurre con mayor frecuencia al usar un proxy corporativo con inspección SSL. Solución: agregue el certificado CA del proxy a los confiables (ver sección anterior) o use SOCKS5 en lugar de un proxy HTTP — SOCKS5 no realiza inspección SSL.
Error 5: Handshake status 403 Forbidden
El servidor de destino bloquea la conexión. Razones: la IP del proxy está en la lista negra, faltan los encabezados necesarios (Origin, User-Agent), o el servidor bloquea el tráfico de centros de datos. Solución: use proxies residenciales con IP reales de usuarios domésticos — son significativamente más difíciles de bloquear.
Error 6: [Errno 111] Connection refused
El servidor proxy no está disponible: host/puerto incorrecto, o el proxy no está en funcionamiento. Verifique los datos de conexión y la disponibilidad del proxy a través de una simple solicitud HTTP antes de probar WebSocket.
Qué tipo de proxy elegir para tareas de WebSocket
La elección del tipo de proxy depende de la tarea específica. Aquí hay una guía práctica:
| Tarea | Tipo recomendado | Por qué |
|---|---|---|
| Parsing a través de WS (intercambios, datos financieros) | Proxies de centros de datos | Alta velocidad, baja latencia, conexión estable |
| Eludir bloqueos de servicios WebSocket | Proxies residenciales | IP reales, riesgo mínimo de bloqueo |
| Aplicaciones móviles con WS (trabajando con APIs móviles) | Proxies móviles | IP de operadores móviles — alta confianza por parte de los servicios |
| Pruebas de carga del servidor WS | Proxies de centros de datos | Barato, rápido, muchas conexiones simultáneas |
| Pruebas de geolocalización WS | Proxies residenciales | Gran variedad de países y ciudades |
Parámetros importantes del proxy para WebSocket
Al elegir un proveedor de proxies para tareas de WebSocket, preste atención a los siguientes parámetros:
- Soporte SOCKS5: asegúrese de que el proveedor ofrezca SOCKS5, no solo HTTP
- Duración de la sesión: para WebSocket, son importantes las sesiones "sticky" — una IP durante un largo tiempo. Los proxies rotativos interrumpirán la conexión
- Tiempo de espera de conexión: el proxy debe soportar conexiones TCP de larga duración (de varios minutos a horas)
- Ancho de banda: para WebSocket de streaming (video, datos de intercambio) es importante un alto ancho de banda sin restricciones
- Latencia: para aplicaciones financieras y bots de trading, la latencia mínima es crítica — elija proxies con servidores más cerca del servicio objetivo
💡 Verificación del soporte de WebSocket por parte del proveedor de proxy
Antes de comprar un proxy para tareas de WebSocket, pruébelo a través de un servidor de eco gratuito:
wss://echo.websocket.org o
wss://ws.postman-echo.com/raw.
Si se establece la conexión y se devuelven los mensajes — el proxy funciona correctamente con WebSocket.
Configuración de sesión sticky para WebSocket
La mayoría de los proveedores de proxies residenciales utilizan rotación de IP por defecto. Para WebSocket, esto es inaceptable — cada cambio de IP significa una interrupción de la conexión. Asegúrese de que está utilizando el modo de sesión sticky (IP fija). Esto generalmente se hace a través de un formato especial de URL de proxy:
# Ejemplo de formato de sesión sticky (depende del proveedor):
# Rotativo (NO adecuado para WebSocket):
socks5://user:[email protected]:1080
# Sesión sticky (adecuado para WebSocket):
socks5://user-session-abc123:[email protected]:1080
# O a través del parámetro de país y sesión:
socks5://user-country-us-session-12345:[email protected]:1080
Conclusión
WebSocket no es simplemente "HTTP con una conexión larga". Es un protocolo separado que requiere un enfoque especial al proxificar. Las principales conclusiones de este artículo son:
- SOCKS5 — la mejor opción para WebSocket: opera a nivel de transporte, no analiza el protocolo, soporta cualquier puerto
- Proxy HTTP a través de CONNECT también funciona, pero con restricciones de puertos y posibles problemas con la inspección SSL
- Las sesiones sticky son obligatorias: los proxies rotativos interrumpirán la conexión WebSocket con cada cambio de IP
- Ping/pong keepalive es necesario para evitar la interrupción de la conexión por tiempo de espera del proxy
- WSS a través de proxy es seguro: el cifrado TLS se establece directamente entre el cliente y el servidor, el proxy no ve el contenido
Si está desarrollando una aplicación que trabaja con WebSocket a través de un proxy — comience con SOCKS5 y sesiones sticky. Esto le ahorrará horas de depuración. Para tareas donde la alta velocidad y estabilidad de la conexión son importantes (bots de trading, streaming de datos), los proxies de centros de datos con baja latencia son una excelente opción. Si el servicio objetivo bloquea activamente las IP de centros de datos — considere los proxies residenciales: tienen IP reales de usuarios domésticos y son significativamente menos propensas a ser bloqueadas incluso durante sesiones WebSocket prolongadas.
```