Objetivos de aprendizaje
Después de leer este artículo podrás:
- Entender cómo el handshake convierte una petición HTTP en una conexión WebSocket
- Escribir un servidor y un cliente WebSocket que funcionan, en Node.js y en el navegador
- Decidir cuándo usar WebSocket y cuándo HTTP o Server-Sent Events encajan mejor
En HTTP normal, el cliente pregunta y el servidor responde. Si el servidor tiene una novedad, no puede avisar: tiene que esperar la siguiente petición. Eso sirve para la página de un producto, pero es incómodo para un chat, un marcador en vivo o un panel de métricas.
WebSocket lo resuelve con una conexión que no se cierra después de la respuesta. Mientras está abierta, cualquiera de los dos lados envía un mensaje cuando quiera, sin una nueva petición. El protocolo está definido en la RFC 6455 y la API del navegador está documentada en MDN.
¿Cómo empieza un WebSocket?#
Con una petición HTTP corriente. El cliente pide cambiar de protocolo y el servidor acepta:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=El estado 101 Switching Protocols marca el cambio. A partir de ahí la misma conexión TCP deja de llevar HTTP y lleva mensajes WebSocket.
La cabecera Sec-WebSocket-Accept demuestra que el servidor entiende el protocolo. Se calcula con SHA-1 sobre el valor de Sec-WebSocket-Key más un GUID fijo de la especificación, en Base64. Así el cliente no confunde un servidor HTTP cualquiera con un servidor WebSocket. No haces esa cuenta a mano: el navegador y las bibliotecas se encargan.
Como empieza siendo HTTP, WebSocket usa los mismos puertos: el 80 para ws:// y el 443 para wss://.
¿Qué viaja después del handshake?#
Tramas (frames). Cada mensaje va en una o más tramas, y el primer byte indica el tipo:
| Tipo | Para qué sirve |
|---|---|
| Texto | Mensaje en UTF-8, normalmente JSON |
| Binario | Bytes crudos: audio, imágenes, formatos como Protobuf |
| Ping y pong | Comprobar que el otro lado sigue vivo |
| Close | Terminar la conexión con un código y un motivo |
Las tramas del cliente al servidor se enmascaran con una clave de 4 bytes, y el servidor debe desconectar a un cliente que envíe una trama sin máscara. La máscara no es cifrado. Para cifrar de verdad, usa wss://.
Un ping puede venir de cualquier lado, y quien lo recibe responde con un pong con el mismo contenido. Las tramas de control llevan como máximo 125 bytes.
¿Cómo se cierra una conexión?#
Uno de los lados envía una trama Close con un código. Los que más vas a ver:
1000: cierre normal.1001: el lado que cierra se va (un servidor que se apaga o un navegador que sale de la página).1006: cierre anormal. La conexión se cayó sin trama Close, por ejemplo por un fallo de red o de un proxy.1008: violación de política.1009: mensaje demasiado grande.
¿Cómo se usa WebSocket en código?#
Este servidor usa la biblioteca ws y reenvía cada mensaje recibido a los demás clientes conectados. Instálala con npm install ws y pon "type": "module" en el package.json:
import { WebSocketServer, WebSocket } from "ws";
const wss = new WebSocketServer({ port: Number(process.env.PORT) || 8080 });
wss.on("connection", (socket) => {
socket.send("conectado");
socket.on("message", (data, isBinary) => {
for (const client of wss.clients) {
if (client !== socket && client.readyState === WebSocket.OPEN) {
client.send(data, { binary: isBinary });
}
}
});
socket.on("close", (code) => console.log("cerrado", code));
});En el navegador la API ya viene incluida, sin nada que instalar:
const socket = new WebSocket("ws://localhost:8080");
socket.addEventListener("open", () => socket.send("hola"));
socket.addEventListener("message", (event) => console.log(event.data));
socket.addEventListener("close", (event) => console.log("cerrado", event.code));Node.js también tiene WebSocket como cliente global (sin la marca de experimental desde la versión 22.4), así que el mismo código funciona en scripts y pruebas. Usa ws:// en desarrollo local y wss:// en producción.
La propiedad socket.readyState informa el estado: 0 conectando (CONNECTING), 1 abierta (OPEN), 2 cerrando (CLOSING) y 3 cerrada (CLOSED). Comprueba WebSocket.OPEN antes de enviar, como en el ejemplo.
¿Cuándo usar WebSocket?#
Cuando los mensajes fluyen en ambos sentidos, en tiempo real y con frecuencia:
- Chat y notificaciones instantáneas.
- Juegos y cursores compartidos en edición colaborativa.
- Paneles en vivo, como cotizaciones y métricas.
Para una API en la que el cliente pide y el servidor responde, una API REST sigue siendo la opción más simple, fácil de cachear y de probar.
¿Cuándo no usar WebSocket?#
Si solo el servidor necesita enviar datos, Server-Sent Events bastan. Son HTTP normal, con Content-Type: text/event-stream, y el navegador se reconecta solo. Si el cliente pregunta de vez en cuando, hacer polling cada pocos segundos es más simple.
WebSocket también tiene costos:
- Estado en el servidor. Cada cliente mantiene una conexión abierta y memoria ocupada. Mil usuarios son mil conexiones.
- Sin reconexión automática. Tú programas el reintento, de preferencia con espera creciente.
- Sin cabeceras personalizadas en el navegador. El constructor
new WebSocket()no acepta cabeceras propias, así que la autenticación suele ir por cookie, por token en la URL (que puede filtrarse en logs) o por un primer mensaje de autenticación. - Sin control de flujo. Según MDN, la API
WebSocketno tiene backpressure: si los mensajes llegan más rápido de lo que la página procesa, la memoria se llena. La alternativaWebSocketStreamlo resuelve, pero no es estándar y tiene soporte limitado. MDN también señala a WebTransport como el reemplazo esperado en muchos usos, con menos soporte en navegadores. - Los proxies cortan conexiones inactivas. Un ping periódico mantiene el tráfico activo.
¿Cómo validar quién se conecta?#
Los navegadores siempre envían la cabecera Origin. Compruébala en el servidor para evitar que otro sitio abra conexiones en nombre del usuario (Cross-Site WebSocket Hijacking). Los clientes que no son navegadores pueden falsificar Origin, así que no sustituye a la autenticación.
¿Qué términos están cerca de WebSocket?#
- Socket TCP: la conexión de bajo nivel. WebSocket corre sobre una y añade el handshake, las tramas y los mensajes.
- Webhook: una llamada HTTP que un servicio hace cuando algo ocurre. No mantiene una conexión abierta.
- Subprotocolo: un formato acordado dentro de WebSocket, negociado en el handshake con el segundo argumento de
new WebSocket(url, protocols).
Para poner en línea un servidor así, lee cómo hospedar un servidor WebSocket. Si queda detrás de un proxy inverso, el proxy debe reenviar la cabecera Upgrade. Detrás de un balanceador de carga, cada conexión queda fija en una instancia. Y el certificado TLS es lo que hace posible wss://.
Cómo hacerlo en Square Cloud#
Un WebSocket necesita un proceso siempre encendido, y así corren las aplicaciones en Square Cloud: en contenedores aislados, sin cold start y sin modo de suspensión. Con el reinicio automático activado (lo predeterminado en el deploy desde el panel), la aplicación vuelve sola cuando el proceso termina con error.
La red de la plataforma admite WebSockets, además de HTTP/3, WAF y mitigación de DDoS, y el proxy no corta conexiones largas. Cada aplicación web recibe un subdominio *.squareweb.app con HTTPS, así que tu cliente abre wss:// directamente en él. Si prefieres un dominio personalizado, está disponible desde el plan Standard.
Haces el deploy del servidor Node.js del ejemplo, ajustas la RAM de la aplicación en su configuración y sigues los logs en tiempo real. Los detalles están en la página de hospedaje de APIs.
Hosting de APIs
APIs en contenedores siempre encendidos, sin cold start
Preguntas frecuentes
¿WebSocket es lo mismo que HTTP?
No. Un WebSocket empieza con una petición HTTP, pero tras el handshake la conexión deja de ser HTTP y pasa a intercambiar mensajes del protocolo WebSocket en ambos sentidos.
¿Qué puerto usa WebSocket?
Por defecto el puerto 80 para ws:// y el 443 para wss://, los mismos que HTTP y HTTPS. Por eso suele atravesar firewalls y puede compartir el puerto de tu servidor web.
¿Cuál es la diferencia entre WebSocket y Server-Sent Events?
Server-Sent Events solo envían datos del servidor al cliente. WebSocket envía en ambos sentidos, en texto o binario, pero tienes que gestionar la reconexión y los latidos por tu cuenta.
¿Debo usar ws:// o wss://?
En producción, wss://, que es WebSocket sobre TLS. Las páginas cargadas por HTTPS suelen tener bloqueado abrir ws:// sin cifrar, y el tráfico abierto puede leerse por el camino.
¿WebSocket se reconecta solo si la conexión se cae?
No. Cuando la conexión se cierra, se dispara el evento close y no ocurre nada más. El código del cliente debe reconectar, de preferencia con una espera creciente entre intentos.
Más en APIs
Ver los 15 artículos- Cómo hospedar una APIHospedar una API es ejecutar su servidor HTTP las 24 horas en una dirección pública con HTTPS. Guía paso a paso con Node.js y Python, pruebas y errores comunes.
- Cómo hospedar una aplicación Node.jsPara hospedar una aplicación Node.js, prepara el package.json, lee el puerto y los secretos del entorno, elige una versión LTS y ejecútala con node. Paso a paso.
- Cómo hospedar FastAPI, Flask o DjangoPara hospedar FastAPI, Flask o Django, ejecuta la aplicación con un servidor de producción (Uvicorn o Gunicorn) en 0.0.0.0. Comandos, ajustes de Django y errores.
- Cómo hospedar una app o script de PythonPara hospedar una app o script de Python, fija la versión, aísla las dependencias en un venv, lee los secretos del entorno y maneja SIGTERM. Guía paso a paso.