Objetivos de aprendizaje
Después de leer este artículo podrás:
- Distinguir quién inicia la comunicación y en qué dirección fluyen los datos en cada opción
- Elegir entre WebSocket, SSE y webhook según el caso de uso
- Ver un ejemplo mínimo en Node.js de las tres técnicas
Una petición HTTP normal es una pregunta con respuesta: el cliente pregunta, el servidor contesta y la conversación termina. Cuando necesitas que algo llegue sin preguntar cada pocos segundos (un mensaje de chat, un pago aprobado, un precio que cambia), aparecen tres opciones: WebSocket, Server-Sent Events (SSE) y webhooks.
Se parecen en el resultado, pero resuelven problemas distintos. La pregunta que ordena todo es: ¿quién habla con quién, y en qué dirección?
¿Qué es cada una?#
WebSocket es un canal bidireccional sobre una sola conexión TCP. Empieza como una petición HTTP con Upgrade, el servidor responde 101 Switching Protocols y desde ahí cliente y servidor se envían mensajes cuando quieran. Usa los esquemas ws:// y wss:// (este último, con TLS). Está definido en la RFC 6455. Más detalle en Qué es WebSocket.
SSE es una respuesta HTTP que no termina. El navegador abre la conexión con EventSource, el servidor responde con Content-Type: text/event-stream y va escribiendo eventos de texto. Solo viaja información del servidor al cliente.
Webhook no es un protocolo, es un patrón: un servicio externo (por ejemplo Stripe o GitHub) hace una petición HTTP POST a una URL tuya cuando ocurre un evento. Es comunicación entre servidores, sin navegador de por medio.
¿Cuáles son las diferencias?#
| WebSocket | SSE | Webhook | |
|---|---|---|---|
| Dirección | Ambos sentidos | Servidor al cliente | Servicio externo a tu servidor |
| Quién inicia | El cliente abre el canal | El cliente abre la conexión | El servicio llama a tu URL |
| Conexión | Persistente | Persistente | Una petición por evento |
| Formato | Texto o binario | Texto UTF-8 | Normalmente JSON |
| Reconexión | La programas tú | Automática en el navegador | Depende del servicio emisor |
| Cliente típico | Navegador, app, otro servidor | Navegador | Otro servidor |
Dos detalles prácticos de SSE: sobre HTTP/1.1 el navegador limita a 6 conexiones abiertas por dominio (por navegador, no por pestaña), mientras que con HTTP/2 el límite negociado por defecto es de 100 streams. Y como EventSource reconecta solo, el servidor puede ajustar la espera con el campo retry.
¿Cuándo elegir WebSocket?#
Cuando el cliente también envía datos con frecuencia y necesitas baja latencia en los dos sentidos: chats, juegos multijugador, edición colaborativa, cursores compartidos. Si solo el servidor habla, WebSocket es más de lo que necesitas.
A cambio, pierdes parte de lo que HTTP ya resuelve: tú gestionas la reconexión, los latidos (ping/pong para detectar conexiones muertas) y la autenticación del mensaje.
// npm install ws
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: Number(process.env.PORT) || 8080 });
wss.on("connection", (socket) => {
socket.on("message", (data) => {
// reenvía a todos los clientes conectados
for (const client of wss.clients) {
if (client.readyState === client.OPEN) client.send(data.toString());
}
});
});Desde el navegador: const ws = new WebSocket("wss://tu-dominio.com"); ws.onmessage = (e) => console.log(e.data);. Para poner este servidor en línea, mira cómo hospedar un servidor WebSocket.
¿Cuándo elegir SSE?#
Cuando solo necesitas empujar datos al navegador: notificaciones, progreso de una tarea, un marcador en vivo, la respuesta de un modelo de IA que llega por trozos. Es HTTP puro, no necesita bibliotecas en el cliente y el navegador reconecta solo.
// npm install express@5
import express from "express";
const app = express();
app.get("/eventos", (req, res) => {
res.set({
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
res.flushHeaders();
const timer = setInterval(() => {
res.write(`event: tick\ndata: ${JSON.stringify({ hora: new Date() })}\n\n`);
}, 1000);
req.on("close", () => clearInterval(timer));
});
app.listen(Number(process.env.PORT) || 3000);const fuente = new EventSource("/eventos");
fuente.addEventListener("tick", (e) => console.log(JSON.parse(e.data)));Cada mensaje termina con una línea en blanco. Los campos reconocidos son event, data, id y retry. Si el cliente necesita enviar algo, usa una petición fetch aparte: la combinación SSE más REST cubre muchas apps que parecen pedir WebSocket.
¿Cuándo elegir un webhook?#
Cuando otro servicio debe avisar a tu backend: un pago confirmado, un push a un repositorio, un mensaje entrante. Evitas consultar su API cada minuto (polling) y recibes el evento al momento. Para el caso de Discord, mira webhook vs bot.
Tu endpoint debe ser público, responder rápido con un 2xx y verificar la firma, porque cualquiera puede hacer un POST a tu URL. GitHub, por ejemplo, firma el cuerpo con HMAC-SHA256 en la cabecera X-Hub-Signature-256 y recomienda una comparación de tiempo constante.
import crypto from "node:crypto";
import express from "express";
const app = express();
// el HMAC se calcula sobre el cuerpo crudo, no sobre el JSON ya parseado
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
if (!Buffer.isBuffer(req.body)) return res.sendStatus(415); // sin cuerpo JSON
const esperado =
"sha256=" +
crypto.createHmac("sha256", process.env.WEBHOOK_SECRET).update(req.body).digest("hex");
const recibido = req.get("X-Hub-Signature-256") ?? "";
const a = Buffer.from(esperado);
const b = Buffer.from(recibido);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.sendStatus(401);
}
const evento = JSON.parse(req.body.toString());
console.debug("evento recibido", evento.action);
res.sendStatus(204);
});
app.listen(Number(process.env.PORT) || 3000);El nombre de la cabecera y el algoritmo cambian según el servicio: revisa su documentación.
¿Se pueden combinar?#
Sí, y es lo habitual. Un caso típico: un proveedor de pagos avisa a tu servidor con un webhook de pago completado, y tu servidor lo reenvía al navegador del usuario con SSE o WebSocket para que la pantalla cambie sin recargar. El webhook trae el dato hasta tu backend; SSE o WebSocket lo llevan hasta el cliente.
Regla corta: si el destino es un navegador y solo escucha, SSE. Si el navegador también habla mucho, WebSocket. Si el destino es tu servidor y el emisor es un servicio externo, webhook. No hay un ganador universal. Si lo que buscas es consultar o modificar datos, una API REST sigue siendo la base.
Cómo hacerlo en Square Cloud#
Los tres patrones necesitan un proceso siempre encendido con una URL pública, y es lo que da una aplicación web en Square Cloud: contenedores sin modo de suspensión y un subdominio *.squareweb.app con HTTPS en todos los planes. La red admite WebSockets, así que el wss:// del ejemplo funciona sobre ese subdominio, y el proxy deja pasar WebSocket y SSE sin buffer y sin cortar conexiones largas.
Para el webhook, la URL de tu aplicación es la que registras en el servicio emisor. Guarda el secreto de firma como variable de entorno en el panel, no en el código. Puedes usar Node.js, Python, Go y otros de los runtimes disponibles.
El plan Hobby arranca en R$12,49 al mes con 1 GB de RAM, y la RAM se comparte entre todas tus aplicaciones. Para publicar una API completa, empieza por Cómo hospedar una API y revisa el checklist de producción. La página de hospedaje de APIs resume lo que necesitas.
Hosting de APIs
APIs en contenedores siempre encendidos, sin cold start
Preguntas frecuentes
¿SSE es más rápido que WebSocket?
No hay una diferencia de velocidad que decida la elección. Una vez abiertos, ambos envían eventos al instante. La diferencia real es la dirección: SSE solo va del servidor al cliente, WebSocket va en los dos sentidos.
¿Puedo usar un webhook para actualizar una página en tiempo real?
No directamente. Un webhook llega a tu servidor, no al navegador. Tu servidor tiene que reenviar el dato al navegador con SSE o WebSocket.
¿WebSocket reemplaza a las peticiones HTTP normales?
No. Para consultar o modificar recursos, una API REST sigue siendo lo más simple. WebSocket es para cuando necesitas mensajes frecuentes en ambos sentidos.
¿SSE se reconecta solo si se corta la conexión?
Sí. EventSource reintenta automáticamente y el servidor puede indicar el tiempo de espera con el campo retry. Con WebSocket tienes que programar la reconexión tú.
¿Cómo sé que un webhook viene del servicio real?
Verificando la firma. Muchos servicios envían un HMAC del cuerpo en una cabecera, por ejemplo X-Hub-Signature-256 en GitHub, y debes compararlo con tu secreto usando una comparación de tiempo constante.
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.