SSE vs WebSockets em 2026: qual usar em cada caso?
Equipe Arvucore
September 22, 2025 · Atualizado em August 26, 2026
16 min read
Se você só precisa empurrar dados do servidor para o browser, use Server-Sent Events: eles rodam sobre HTTP puro, reconectam sozinhos, atravessam proxies e plataformas serverless, e dão conta de notificações, dashboards e streaming de tokens de LLM. Use WebSockets quando o cliente também precisa enviar muitas mensagens pequenas com baixa latência, ou quando você precisa de frames binários, como em chat, edição colaborativa e jogos. Polling e long polling continuam sendo fallbacks, não primeira escolha.
Como SSE e WebSockets funcionam no nível do protocolo
Server-Sent Events são HTTP comum. O browser envia um GET, o servidor responde com Content-Type: text/event-stream e nunca fecha a resposta. Os eventos são linhas de texto UTF-8: data:, event: e id: opcionais, separados por uma linha em branco. Qualquer coisa capaz de fazer streaming de uma resposta HTTP consegue servir SSE.
No HTTP/1.1 isso tem um custo conhecido: os browsers limitam as conexões por origem a seis. Cada EventSource aberto ocupa uma delas, então uma página com vários streams, ou um usuário com várias abas, pode esgotar o orçamento e travar as requisições normais. Sobre HTTP/2 o problema desaparece: cada stream SSE é um stream multiplexado dentro de uma única conexão TCP, e o limite por origem fica na casa de uma centena de streams, negociado com o servidor. Em 2026 o limite morde principalmente em desenvolvimento local e atrás de proxies legados que rebaixam para HTTP/1.1.
WebSockets começam como HTTP e deixam de ser HTTP. O cliente envia um GET com Upgrade: websocket e Connection: Upgrade; o servidor responde 101 Switching Protocols, e a partir daí o socket TCP carrega frames WebSocket (RFC 6455): texto ou binário, com opcodes, fragmentação, mascaramento do cliente para o servidor e frames de controle ping/pong. Sobre HTTP/2 existe um mecanismo separado (RFC 8441, o método CONNECT estendido) que tunela um WebSocket dentro de um stream HTTP/2, mas o suporte em servidores, proxies e bibliotecas é inconsistente, então a maioria das implantações ainda roda WebSockets em uma conexão HTTP/1.1 dedicada.
Tudo que vem depois reage a essa diferença: um stream SSE é uma resposta HTTP longa; um WebSocket é um socket opaco que as ferramentas de HTTP já não entendem.
Reconexão, ordenação e Last-Event-ID
É aqui que o SSE justifica a fama de simples. O EventSource reconecta automaticamente quando a conexão cai, usando o intervalo retry: que o servidor pode definir. Se o servidor marca os eventos com id:, o browser devolve o último como header Last-Event-ID na reconexão. O servidor reenvia o que o cliente perdeu a partir de um log, de um Redis Stream ou de um offset do Kafka. Retomada faz parte da spec.
WebSockets não têm nada disso. Quando o socket fecha, o objeto WebSocket está morto. Seu código precisa detectar o fechamento, aplicar backoff com jitter para que milhares de clientes não reconectem no mesmo instante, reautenticar, reassinar e reconciliar o estado. Se você precisa de "me dê o que eu perdi", projeta isso por conta própria com números de sequência ou um ID de sessão retomável.
Duas observações para os dois transportes. Conexões ociosas morrem: operadoras, tabelas de NAT e load balancers derrubam sockets em silêncio por dezenas de segundos a alguns minutos, então envie um heartbeat (um comentário SSE : ping ou um frame ping do WebSocket) dentro dessa janela. E o EventSource tenta de novo para sempre, por design, então um erro permanente como um 401 precisa ser tratado explicitamente.
Proxies, load balancers e CDNs
SSE parece uma resposta HTTP lenta. Todo proxy L7, ingress e CDN a encaminha, mas vários fazem buffer das respostas por padrão, o que transforma um stream em nada até o buffer encher. Desative o buffering por rota (X-Accel-Buffering: no no Nginx, o equivalente no seu ingress ou CDN), desative a compressão no stream ou use um compressor que faça flush, e aumente os timeouts de ociosidade e leitura para esse caminho. A maioria dos incidentes de "SSE não funciona atrás do nosso proxy" é uma dessas três configurações.
WebSockets precisam que o proxy respeite o handshake Upgrade e deixe o socket passar. Proxies modernos, load balancers de nuvem e CDNs fazem isso, muitas vezes com condições: planos específicos, timeouts de ociosidade que você não pode aumentar, ou nenhum suporte em um middlebox corporativo mais antigo. Redes restritivas ainda podem bloquear wss://, e é por isso que existem fallbacks de long polling. Algumas CDNs cobram conexões WebSocket de forma diferente das requisições HTTP. Passthrough L4 evita quase tudo isso, mas abre mão do roteamento e da observabilidade L7.
HTTP/3 e QUIC mudam pouco aqui por enquanto: SSE funciona sobre HTTP/3 como qualquer resposta, enquanto WebSocket sobre HTTP/3 (RFC 9220) ainda raramente está implantado de ponta a ponta.
Autenticação: cookies vs headers
Nenhuma das duas APIs do browser permite definir um header Authorization. Isso surpreende equipes com um setup limpo de bearer token para a API REST que depois descobrem que o canal em tempo real não consegue reaproveitá-lo.
Suas opções:
- Cookies. Tanto o
EventSource(comwithCredentials: truepara cross-origin) quanto oWebSocketenviam cookies. É o caminho mais simples quando o front-end é same-site. DefinaSameSite,SecureeHttpOnly, e valide o headerOriginno servidor, porque WebSockets não são cobertos pelo CORS. - Token na query string. Funciona nos dois, mas o token vai parar em logs de acesso, logs de proxy e histórico do browser. Use um ticket de curta duração e propósito único emitido por um
POSTautenticado, não o token de sessão de longa duração. - Token na primeira mensagem (só WebSocket). Abra o socket, envie
{ "type": "auth", "token": "..." }e feche se ele não chegar em um segundo. É padrão na prática; ainda assim, a conexão existe por um breve momento sem autenticação. - Substituir o EventSource por
fetch. Umfetchcom streaming viaresponse.body.getReader()aceita qualquer header, funciona comAbortControllere consegue interpretartext/event-streamcom poucas linhas de código ou uma biblioteca pequena. Você perde a reconexão automática e oLast-Event-ID, que precisa reimplementar. É o padrão que a maioria das interfaces de chat com LLM usa hoje.
O header Sec-WebSocket-Protocol às vezes é abusado para carregar um token; funciona, mas amarra a autenticação a um campo pensado para negociação de subprotocolo. Para tokens e sessões em geral, veja autenticação moderna com OAuth 2.0, JWT e zero trust.
Escala e fan-out: pub/sub, Redis, sticky sessions
Um único nó consegue manter de dezenas a centenas de milhares de conexões ociosas, dependendo do runtime, da memória por conexão e do TLS. O problema nunca é a quantidade; é que uma mensagem produzida no nó A precisa chegar a um cliente conectado no nó B.
A resposta padrão é a mesma para os dois transportes: mantenha os nós de conexão sem estado e coloque uma malha de pub/sub atrás deles. Redis Pub/Sub ou Redis Streams, NATS ou Kafka carregam os eventos; cada nó assina os canais que interessam aos seus clientes e escreve nos sockets locais. Redis Streams e Kafka também dão o log de replay de que o Last-Event-ID precisa. Projete o modelo de tópicos cedo; assinaturas sem limite são a causa usual de crescimento de memória.
Onde os transportes diferem:
- SSE balanceia como qualquer requisição HTTP. Qualquer nó pode atender qualquer cliente, e uma reconexão pode cair em qualquer lugar, porque o estado de retomada viaja no
Last-Event-ID. Sticky sessions são opcionais. - WebSockets têm estado por natureza. Se o servidor mantém estado por conexão (assinaturas, presença, cursores) em memória, uma reconexão precisa voltar ao mesmo nó, o que significa sticky sessions, ou esse estado precisa ser externalizado. Gateways gerenciados e frameworks com backplane em Redis existem principalmente para resolver isso.
- Backpressure é uma preocupação do WebSocket nas duas direções e do SSE em uma. Limite as filas por conexão, agrupe atualizações para clientes lentos, e descarte ou degrade em vez de deixar o
bufferedAmountcrescer.
Deploys são um evento de escala disfarçado: um rollout fecha todas as conexões do nó, e dezenas de milhares de clientes reconectam de uma vez. Drene com cuidado, espalhe os restarts e garanta que o backoff do cliente tenha jitter.
Serverless, browsers e mobile
Serverless. Funções têm escopo de requisição e limite de tempo. WebSockets não conseguem viver dentro de uma função normal; você precisa de um gateway gerenciado que segure o socket e invoque funções por mensagem, mais um store para os IDs de conexão. SSE encaixa melhor: uma função de streaming ou de edge retorna um ReadableStream e o mantém aberto até o limite da plataforma, normalmente minutos, com o cliente reconectando em seguida. Primitivas no estilo actor, como Durable Objects, seguram WebSockets entre requisições onde você precisa. Veja computação serverless para o modelo de custo que torna conexões longas caras na cobrança por invocação.
Browsers. As duas APIs são suportadas em todos os browsers atuais, desktop e mobile. O EventSource é só texto e só GET; o WebSocket suporta binário e não tem essas restrições.
Apps mobile. As plataformas nativas suspendem apps em segundo plano e fecham seus sockets. Nenhum transporte sobrevive a isso; tudo que precisa alcançar um usuário em segundo plano ou offline passa por APNs, FCM ou Web Push, com o canal ao vivo usado só em primeiro plano. Bibliotecas de WebSocket são maduras no iOS e no Android; os clientes SSE são mais enxutos, e muitas equipes usam uma requisição HTTP com streaming. O custo de bateria é ditado pelos despertares do rádio, então um heartbeat de 20 segundos pesa mais do que a escolha do protocolo.
Desvantagens do WebSocket: os custos, explicitamente
WebSockets são poderosos e, com frequência, o padrão errado. Os custos específicos:
- Sem reconexão ou retomada embutida. Toda equipe reimplementa backoff, reassinatura e recuperação de mensagens perdidas.
- Sem headers customizados a partir do browser. Auth é por cookies, query string ou handshake na primeira mensagem.
- Conexões com estado. Sticky sessions ou um store de estado externo; deploys blue-green e rolling ficam mais difíceis.
- Encaixe ruim com serverless. Precisa de um gateway dedicado e de um registro de conexões; a cobrança por minuto de conexão acumula.
- Fora do ferramental HTTP. Sem cache HTTP, sem compressão padrão a menos que
permessage-deflateseja negociado, sem log de requisição por mensagem, e observabilidade exige instrumentação customizada. - Atrito com proxies e redes. O Upgrade precisa ser respeitado de ponta a ponta; algumas redes corporativas e planos de CDN bloqueiam ou limitam.
- Sem proteção do CORS. Sequestro de WebSocket cross-site é real se você depende de cookies sem checar o
Origin. - Protocolo customizado por cima. Framing de mensagens, versionamento e semântica de erro são seus para definir e manter compatíveis com versões anteriores.
- Head-of-line blocking em uma única conexão TCP. Um frame grande atrasa tudo que vem atrás; não há priorização de streams.
- Superfície operacional maior. Limites de conexão, file descriptors, CPU de TLS e testes de carga exigem ferramentas que entendam o protocolo.
Nada disso é desqualificante para chat ou colaboração. É a conta da bidirecionalidade; pague só quando for usá-la.
Polling e long polling: os fallbacks
Short polling dispara uma requisição num timer. É universalmente compatível, cacheável e a resposta certa para dados que mudam a cada poucos minutos. A uma requisição por segundo por cliente vira a opção mais cara de todas, com latência limitada pelo intervalo.
Long polling mantém a requisição aberta até chegar dado ou estourar um timeout, e então o cliente pede de novo. Aproxima push sobre HTTP puro e funciona atrás de quase tudo. O custo é um round trip HTTP completo por mensagem, uma lacuna entre respostas em que eventos se perdem a menos que você carregue um cursor, e os mesmos limites de conexão do HTTP/1.1 que o SSE, sem a reconexão definida pela spec.
Em 2026 long polling é um fallback para redes que quebram tanto SSE quanto WebSockets, não um design principal. Se a sua biblioteca ainda o usa por padrão, verifique o motivo.
Tabela comparativa: SSE vs WebSockets vs long polling
| Critério | Server-Sent Events | WebSockets | Long polling |
|---|---|---|---|
| Direção | Servidor para cliente (cliente usa requisições normais) | Full duplex | Iniciado pelo cliente, servidor segura |
| Transporte | Stream de resposta HTTP/1.1, HTTP/2, HTTP/3 | Socket TCP com upgrade; túnel HTTP/2 raramente implantado | Requisições HTTP simples |
| Reconexão | Automática, embutida no EventSource |
Manual | Manual (a cada requisição) |
| Retomada após queda | Last-Event-ID, definido na spec |
Números de sequência customizados | Cursor customizado |
| Suporte a binário | Não (texto, base64 se necessário) | Sim, frames nativos | Sim (corpo da resposta) |
| Auth no browser | Cookies, ticket na query ou fetch com headers |
Cookies, ticket na query, primeira mensagem | Qualquer header |
| Amigável a proxy e CDN | Alta; desative buffering e aumente timeouts | Média; precisa de suporte a Upgrade de ponta a ponta | A mais alta |
| Limite por origem no HTTP/1.1 | 6 conexões; resolvido pelo HTTP/2 | Conexão separada para cada um | 6 conexões |
| Modelo de escala | Nós sem estado mais pub/sub, sem stickiness | Pub/sub mais sticky sessions ou estado externo | Sem estado |
| Encaixe com serverless | Bom em funções de streaming ou edge | Exige gateway gerenciado | Bom |
| Overhead por mensagem | Baixo (poucos bytes por evento) | O menor (2 a 14 bytes de framing) | Alto (HTTP completo por mensagem) |
| Melhores casos de uso | Notificações, dashboards, feeds, streaming de LLM | Chat, edição colaborativa, jogos, trading | Fallback, atualizações de baixa frequência |
Código: cliente EventSource, endpoint SSE em Node, echo WebSocket
Um endpoint SSE mínimo em Node, sem frameworks. Repare nos headers que impedem os proxies de fazer buffer e no id: que habilita a retomada.
// server.js — Node 20+, sem dependências
import http from "node:http";
http.createServer((req, res) => {
if (req.url !== "/events") { res.writeHead(404).end(); return; }
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
});
let id = Number(req.headers["last-event-id"] ?? 0);
const timer = setInterval(() => {
id += 1;
res.write(`id: ${id}\nevent: tick\ndata: ${JSON.stringify({ t: Date.now() })}\n\n`);
}, 1000);
const heartbeat = setInterval(() => res.write(": ping\n\n"), 15000);
req.on("close", () => { clearInterval(timer); clearInterval(heartbeat); });
}).listen(3000);
O lado do browser são poucas linhas, e a reconexão vem de graça.
const es = new EventSource("/events", { withCredentials: true });
es.addEventListener("tick", (e) => console.log(JSON.parse(e.data)));
es.onerror = () => console.warn("disconnected, browser will retry");
Um echo WebSocket mínimo com o pacote ws. Compare o que o cliente precisa fazer quando o socket cai.
// ws-server.js
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: 3001 });
wss.on("connection", (socket, req) => {
if (req.headers.origin !== "https://app.example.com") { socket.close(1008); return; }
socket.on("message", (data) => socket.send(data));
});
// client
let ws, attempt = 0;
function connect() {
ws = new WebSocket("wss://api.example.com/ws");
ws.onopen = () => { attempt = 0; ws.send("hello"); };
ws.onmessage = (e) => console.log(e.data);
ws.onclose = () => setTimeout(connect, Math.min(30000, 500 * 2 ** attempt++) * (0.5 + Math.random()));
}
connect();
Os dois servidores devem ficar atrás de TLS em produção; nenhum dos exemplos trata autenticação além de uma checagem de Origin.
Checklist de decisão: quando usar cada um
- Chat (1:1, grupo, suporte). WebSockets. As mensagens fluem nas duas direções o tempo todo, e indicadores de digitação e presença são baratos num socket aberto. SSE mais
POSTfunciona para widgets de suporte de baixo volume. - Dashboards ao vivo e monitoramento. SSE. Unidirecional, texto, tolera um segundo de reconexão. Agrupe atualizações no servidor; veja desenvolvimento de dashboards para o lado dos dados.
- Notificações e feeds de atividade. SSE enquanto a aba está aberta; Web Push, APNs ou FCM quando não está. Nunca um WebSocket só para notificações.
- Edição colaborativa. WebSockets. Sincronização com CRDT ou OT precisa de mensagens pequenas, frequentes e bidirecionais, muitas vezes com codificação binária.
- Jogos multiplayer. WebSockets hoje; WebTransport onde você pode exigir HTTP/3 e quer datagramas não confiáveis para atualizações de posição.
- Streaming de tokens de LLM. SSE, ou um
fetchcom streaming interpretandotext/event-streamquando você precisa de headers e cancelamento. É o que as principais APIs de modelos expõem, e compõe bem com serverless. - Trading e dados de mercado. WebSockets para fluxo de ordens; SSE é aceitável para tickers somente leitura. Fan-out via Redis ou Kafka em qualquer um dos casos.
- Telemetria de IoT para o browser. MQTT sobre WebSockets a partir dos dispositivos, pub/sub no meio, SSE ou WebSockets até o dashboard dependendo de os operadores enviarem comandos ou não.
- Voz ou vídeo. Nenhum dos dois. Use WebRTC, com WebSockets ou SSE apenas para sinalização.
- Redes corporativas restritivas. SSE primeiro, long polling como último recurso.
WebTransport: a opção emergente
WebTransport é a API do browser sobre HTTP/3 e QUIC: múltiplos streams independentes sem head-of-line blocking, e datagramas não confiáveis para dados em que chegar atrasado é pior do que se perder. Para jogos, mídia e telemetria de alta frequência, ele remove os dois limites estruturais dos WebSockets: um único stream TCP e entrega apenas confiável.
Adoção é o problema. O suporte nos browsers é amplo no Chromium e vem melhorando nos outros, mas servidores, load balancers e CDNs ficam para trás, e redes hostis a UDP precisam de um caminho WebSocket de qualquer forma. Em 2026 ele se justifica para um conjunto estreito de produtos e é item de pesquisa para todo o resto. Abstraia o transporte para que ele possa mudar; não planeje a migração ainda.
Recomendação
Comece com SSE para tudo que é do servidor para o cliente, combinado com requisições HTTP normais na outra direção. Sirva sobre HTTP/2, desative o buffering do proxy, defina id: em todo evento e apoie tudo num log com replay para que o Last-Event-ID recupere o que foi perdido. Mova uma funcionalidade para WebSockets só quando ela precisar de tráfego bidirecional de alta frequência ou frames binários, e reserve orçamento para a lógica de reconexão, um handshake de auth, uma camada de pub/sub e sticky sessions ou estado externalizado. Mantenha o long polling como fallback atrás de uma flag, não como design. Na Arvucore, costumamos recomendar um transporte por funcionalidade, escolhido pelo checklist acima, em vez de um para o produto inteiro; os sistemas que envelhecem bem são aqueles em que a arquitetura é a camada de pub/sub, não o socket.
Pronto para Transformar seu Negócio?
Vamos conversar sobre como nossas soluções podem ajudá-lo a alcançar seus objetivos. Entre em contato com nossos especialistas hoje mesmo.
Falar com um EspecialistaTags:
Equipe Arvucore
A equipe editorial da Arvucore é formada por profissionais experientes em desenvolvimento de software. Somos dedicados a produzir e manter conteúdo de alta qualidade que reflete as melhores práticas da indústria e insights confiáveis.
Perguntas frequentes
- SSE é melhor que WebSockets?
- Nenhum dos dois é melhor em geral. SSE é mais simples e serve bem a fluxos unidirecionais do servidor para o cliente, como notificações, dashboards e streaming de tokens de LLM. WebSockets são a escolha certa quando o cliente também precisa enviar mensagens frequentes com baixa latência, ou quando você precisa de frames binários.
- Quais são as principais desvantagens dos WebSockets?
- Sem reconexão ou retomada automática, sem headers customizados no handshake do browser, conexões com estado que exigem sticky sessions ou uma camada de pub/sub, encaixe ruim com serverless e com alguns proxies ou CDNs, e um protocolo separado que passa ao largo das ferramentas, do cache e da compressão padrão do HTTP.
- SSE funciona sobre HTTP/2?
- Sim. Sobre HTTP/2, cada stream SSE é um stream multiplexado em uma única conexão TCP, o que elimina o limite de seis conexões por origem que prejudica o SSE no HTTP/1.1. A maioria dos browsers e provedores de edge negocia HTTP/2 por padrão com TLS.
- Dá para enviar headers customizados com EventSource ou WebSocket no browser?
- Não. Nenhuma das duas APIs do browser permite definir um header Authorization. Use cookies, um token de curta duração na query string, ou substitua o EventSource por fetch com um ReadableStream, que aceita headers.
- Long polling ainda é relevante em 2026?
- Só como fallback para redes restritivas ou atualizações de frequência muito baixa. Funciona em qualquer lugar, mas desperdiça requisições, adiciona latência e é mais difícil de raciocinar do que o SSE, que todo browser moderno suporta.
- WebTransport vai substituir os WebSockets?
- Ainda não. WebTransport roda sobre HTTP/3 e QUIC e oferece datagramas não confiáveis e múltiplos streams sem head-of-line blocking, mas o suporte em browsers e infraestrutura ainda é irregular. Trate como opção para jogos e mídia, não como padrão.
Artigos relacionados

Desenvolvimento WebRTC em 2026: arquitetura, custo e escopo
O que o WebRTC resolve e o que você precisa construir: P2P vs SFU vs MCU, TURN, plataformas gerenciadas vs self-hosted, segurança e o que define o custo.

Acessibilidade (A11y) no Desenvolvimento Web: Diretrizes WCAG 2.1
Como guia da Arvucore, este artigo explica a Acessibilidade (A11y) no desenvolvimento web e as diretrizes WCAG 2.1, oferecendo conselhos práticos para tomadores de decisão europeus e equipes técnicas. Destaca como o desenvolvimento de acessibilidade web melhora a experiência do usuário, conformidade legal e alcance de mercado, incluindo considerações de design que integram acessibilidade desde o início dos ciclos de vida do produto.

Arquitetura Hexagonal em 2026: Portas, Adaptadores, Clean
Hexagonal vs Clean vs Onion explicadas com um exemplo real em TypeScript, estrutura de pastas, estratégia de testes e os erros que afundam a maioria dos times.

Jamstack em 2026: o que sobreviveu e o que o substituiu
O rótulo Jamstack sumiu, mas as práticas venceram. Estratégias de renderização comparadas, frameworks, hospedagem e um checklist para saber quando estático é a escolha certa.