WebSockets vs SSE: el error está en elegir “tiempo real” antes que arquitectura

Durante años, WebSockets ha sido la respuesta automática a casi cualquier requisito de tiempo real. Un dashboard con precios en directo, notificaciones, logs, inventario, alertas, estado de pedidos o respuestas de inteligencia artificial que aparecen palabra a palabra suelen llevar a la misma decisión: abrir un WebSocket y mantenerlo vivo. Parece la opción seria, la moderna, la que cualquier equipo técnico elegiría sin demasiada discusión.

Pero esa decisión no siempre es la mejor. Server-Sent Events, o SSE, sigue siendo una alternativa muy útil cuando el flujo de datos va en una sola dirección: del servidor al cliente. Funciona sobre HTTP, está soportado de forma nativa por el navegador, permite reconexión automática con EventSource y evita parte de la complejidad operativa que aparece cuando se despliegan WebSockets a escala.

El debate se ha reactivado porque algunos equipos han medido diferencias relevantes en memoria cuando escalan a decenas o cientos de miles de conexiones. En determinados escenarios, SSE puede consumir menos recursos y simplificar la infraestructura. Pero otros benchmarks muestran lo contrario: WebSockets puede comportarse mejor en memoria y rendimiento, incluso en comunicaciones unidireccionales, dependiendo del framework, el servidor, el tamaño de mensaje, la frecuencia de envío y la implementación concreta.

La conclusión útil no es que SSE gane siempre ni que WebSockets sea la opción correcta por defecto. La conclusión es más incómoda: hay que elegir por patrón de comunicación, infraestructura y pruebas propias, no por costumbre.

Dos tecnologías parecidas para problemas distintos

WebSockets crea una conexión persistente bidireccional entre cliente y servidor. Una vez establecida, ambas partes pueden enviar y recibir mensajes en cualquier momento. Es ideal para chats, editores colaborativos, juegos multijugador, sistemas con eventos frecuentes en ambos sentidos, herramientas de trading interactivas o aplicaciones donde el cliente necesita enviar señales constantes al servidor.

SSE mantiene una conexión HTTP abierta en la que el servidor envía eventos al cliente. El cliente escucha. No hay comunicación bidireccional sobre el mismo canal. Si el usuario necesita enviar algo, lo normal es hacerlo mediante una petición HTTP normal, por ejemplo un POST, y dejar el stream SSE para la respuesta o las actualizaciones.

Ese patrón encaja muy bien con respuestas de modelos de inteligencia artificial, dashboards, feeds, logs de despliegue, barras de progreso, métricas, alertas o notificaciones pasivas. En esos casos, el cliente no necesita hablar todo el tiempo. Solo necesita recibir.

Caso de usoOpción más natural
Chat con escritura en ambos sentidosWebSockets
Editor colaborativoWebSockets
Juego multijugadorWebSockets
Dashboard de métricasSSE o WebSockets
Respuesta de IA token a tokenSSE
Logs de despliegueSSE
Notificaciones pasivasSSE
Datos binarios frecuentesWebSockets
Feed de mercado con interacción intensaDepende de frecuencia y carga

La diferencia de arquitectura importa porque termina afectando a memoria, balanceadores, proxies, reconexión, autenticación, observabilidad y coste.

SSE gana cuando el servidor habla y el cliente escucha

SSE tiene una virtud muy clara: es simple. En su forma básica, basta con devolver una respuesta HTTP con Content-Type: text/event-stream y escribir eventos conforme se producen. El navegador puede recibirlos con EventSource, reconectar si se cae la conexión y gestionar identificadores de evento si el servidor implementa reanudación.

Ese diseño reduce código y elimina decisiones que en WebSockets suelen aparecer pronto: reconexión, backoff, heartbeat, estado de sesión, gestión de canales, compatibilidad con proxies y librerías adicionales. En muchos productos, toda esa complejidad se añade antes de saber si realmente hacía falta.

Las respuestas generativas de IA son un ejemplo claro. El usuario envía un prompt mediante una petición normal y el servidor devuelve tokens por streaming. No hace falta mantener un canal bidireccional completo para cada conversación si el patrón principal es “el usuario pide, el servidor genera y el cliente pinta”.

SSE también se integra mejor con parte de la infraestructura HTTP existente. Headers, cookies, monitorización, trazas, balanceadores y herramientas de observabilidad entienden HTTP. Aun así, “es HTTP” no significa “funciona sin tocar nada”. Nginx puede bufferizar eventos, algunos balanceadores no están bien preparados para conexiones largas y ciertos frameworks bloqueantes pueden agotar hilos si cada conexión abierta consume un worker.

Ventaja de SSEImplicación práctica
Funciona sobre HTTPMenos fricción con infraestructura existente
API nativa EventSourceImplementación muy rápida en navegador
Reconexión automáticaMenos código de cliente
Adecuado para server-to-clientPerfecto para streaming pasivo
Modelo sencilloMenos estado que mantener

SSE no es una tecnología menor. Es una herramienta que muchos equipos olvidan porque WebSockets se convirtió en sinónimo de “real-time”.

WebSockets vs SSE: el error está en elegir “tiempo real” antes que arquitectura | websockets vs sse tabla
WebSockets vs SSE: el error está en elegir “tiempo real” antes que arquitectura

WebSockets gana cuando hay ida y vuelta real

WebSockets sigue siendo imprescindible cuando la aplicación necesita comunicación frecuente en ambos sentidos. Un chat con indicadores de escritura, un editor colaborativo, una sesión de juego, una herramienta de trading interactiva o un sistema de control remoto no encajan bien en SSE. Forzar SSE en esos casos suele terminar en una mezcla de stream para recibir y peticiones paralelas para enviar, con más complejidad de la necesaria.

También puede tener ventaja en rendimiento cuando se usan implementaciones muy optimizadas. WebSockets trabaja con framing propio y puede manejar mejor datos binarios. En cargas con mensajes pequeños y frecuentes, algunos tests muestran menor uso de memoria frente a SSE. Si además se usan servidores especializados como uWebSockets.js o implementaciones muy ajustadas en Go, Rust o C++, el techo de conexiones concurrentes puede ser superior.

El problema aparece cuando se elige WebSockets para un caso que no lo necesita. Entonces el equipo asume estado persistente, reconexión manual, backpressure, heartbeat, escalado horizontal y configuración de infraestructura sin obtener una ventaja clara. Es como instalar una autopista para mover una cinta transportadora de un solo sentido.

El rendimiento depende demasiado del contexto

Los titulares del tipo “SSE usa un 40 % menos de memoria” o “WebSockets siempre rinde mejor” deben leerse con cuidado. Ambos pueden ser ciertos en escenarios distintos.

En una prueba con 100.000 conexiones donde el servidor solo empuja actualizaciones poco frecuentes, SSE puede resultar más eficiente si la implementación HTTP es ligera y el framework maneja bien conexiones largas. En otro entorno, con Socket.IO, NestJS, Kubernetes, HTTP/2, payloads pequeños o grandes y pruebas controladas, WebSockets puede consumir menos memoria y comportarse mejor, incluso cuando se usa solo para envío desde servidor a cliente.

La diferencia no está solo en el protocolo. Está en todo lo que lo rodea: runtime, librería, proxy, kernel, TLS, HTTP/1.1 o HTTP/2, tamaño de mensaje, frecuencia, serialización, gestión de buffers, heartbeats, reconexión, GC, límites de contenedor y observabilidad.

VariablePor qué cambia el resultado
Frecuencia de mensajesNo es igual un evento por segundo que cientos por minuto
Tamaño del payload100 bytes y 50 KB ejercen presiones distintas
FrameworkNode, Go, Java, Python o PHP manejan conexiones de forma diferente
LibreríaSocket.IO no equivale a WebSocket puro
ProxyNginx o balanceadores pueden bufferizar o cortar conexiones
HTTP/2Multiplexa streams, pero puede introducir comportamientos específicos
AutenticaciónCookies, headers y tokens cambian la implementación
ReconexiónPuede generar tormentas si no se controla bien

Por eso la recomendación sana es medir con la carga esperada. No con una demo. No con una tabla genérica. Con el patrón real de la aplicación.

El problema de EventSource: cómodo, pero limitado

La API nativa EventSource es muy cómoda, pero tiene limitaciones conocidas. No permite enviar headers personalizados, no soporta POST, ofrece manejo de errores limitado y obliga a trabajar con GET. Para muchas aplicaciones internas puede ser suficiente, especialmente si se usan cookies HttpOnly para autenticación. Para APIs con bearer tokens, clientes no navegador o flujos más complejos, puede quedarse corta.

Una alternativa moderna es usar fetch con streams y parsear el formato text/event-stream con una librería como eventsource-parser. Así se recupera control sobre headers, método, errores y credenciales. A cambio, el equipo debe implementar reconexión, backoff, limpieza, control de Last-Event-ID y gestión de fallos.

EnfoqueVentajaCoste
EventSource nativoMuy simple y reconecta soloSin headers custom ni POST
SSE con fetch streamsControl total sobre request y erroresHay que implementar reconexión
WebSocket puroBidireccional y flexibleMás gestión de estado
Socket.IOReconexión y abstracción incluidasMás dependencia y overhead

En producción, muchas veces la simplicidad inicial de SSE debe completarse con detalles que los tutoriales no explican: heartbeats, timeouts, desactivación de buffering, límites de conexión y estrategia de reanudación.

Reglas prácticas para elegir

La primera regla es empezar por la dirección del flujo. Si el servidor emite y el cliente escucha, SSE merece estar en la conversación. Si cliente y servidor hablan constantemente, WebSockets será más natural.

La segunda es mirar la frecuencia. Para eventos ocasionales, dashboards, notificaciones y streaming de texto, SSE suele ser suficiente. Para interacciones muy frecuentes y baja latencia en ambos sentidos, WebSockets tiene más sentido.

La tercera es revisar la infraestructura. En Nginx, por ejemplo, hay que desactivar buffering para que los eventos SSE no se acumulen y lleguen tarde. También conviene configurar timeouts y mensajes heartbeat. En entornos serverless o con balanceadores gestionados, las conexiones largas pueden tener límites que cambian la decisión.

La cuarta es no ignorar el framework. Un servidor que asigna un hilo por conexión puede sufrir tanto con SSE como con WebSockets. Node.js, Go o servidores async bien configurados suelen manejar mejor este patrón. Pero si una parte del handler hace I/O síncrono, el resultado puede degradarse igual.

Menos dogma, más pruebas

El error habitual no es elegir WebSockets. WebSockets es una gran tecnología. El error es elegirlo antes de entender el problema. También sería un error elegir SSE por moda inversa, pensando que siempre será más barato o más ligero.

Para un producto de IA con streaming de tokens, SSE encaja muy bien. Para un dashboard con actualizaciones pasivas cada segundo, también. Para una app colaborativa, no. Para una plataforma con 100.000 conexiones, el debate no puede resolverse con teoría: hay que simular usuarios, payloads, frecuencia, reconexiones, picos y fallos de red.

La arquitectura correcta no es la más popular. Es la que hace el trabajo con menos complejidad y coste aceptable. A veces será Content-Type: text/event-stream y un res.write(). Otras será WebSockets con un servidor optimizado, backpressure bien resuelto y una capa de escalado diseñada desde el principio.

La lección es sencilla: el tiempo real no es una tecnología. Es un requisito. Y como cualquier requisito serio, exige medir antes de casarse con una solución.

Preguntas frecuentes

¿SSE puede sustituir a WebSockets?
Solo en casos unidireccionales donde el servidor envía datos y el cliente escucha. Para comunicación bidireccional frecuente, WebSockets sigue siendo la opción natural.

¿SSE consume menos memoria que WebSockets?
Depende de la implementación, carga, framework y patrón de mensajes. Algunos equipos han visto menor consumo con SSE a gran escala, mientras que otros benchmarks muestran mejor comportamiento de WebSockets.

¿Por qué se usa SSE en respuestas de IA en streaming?
Porque el usuario envía una petición y el servidor devuelve tokens progresivamente. Ese patrón es principalmente server-to-client, por lo que SSE encaja bien.

¿Qué hay que configurar para usar SSE en producción?
Conviene desactivar buffering en proxies como Nginx, ajustar timeouts, añadir heartbeats, revisar autenticación, controlar reconexión y asegurar que el framework soporta conexiones largas sin bloquear recursos.

Suscríbete al boletín SysAdmin

Este es tu recurso para las últimas noticias y consejos sobre administración de sistemas, Linux, Windows, cloud computing, seguridad de la nube, etc. Lo enviamos 2 días a la semana.

¡Apúntate a nuestro newsletter!


– patrocinadores –

Noticias destacadas

– patrocinadores –

¡SUSCRÍBETE AL BOLETÍN
DE LOS SYSADMINS!

Scroll al inicio
×