O que é WebSocket e como funciona

WebSocket é um protocolo que mantém uma única conexão aberta entre cliente e servidor, por onde os dois lados enviam mensagens a qualquer momento. Ele começa como uma requisição HTTP e vira uma conexão persistente.

Atualizado em 5 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Entender como o handshake transforma uma requisição HTTP em uma conexão WebSocket
  • Escrever um servidor e um cliente WebSocket que funcionam, em Node.js e no navegador
  • Decidir quando usar WebSocket e quando HTTP ou Server-Sent Events resolvem melhor
Nesta página

No HTTP comum, o cliente pergunta e o servidor responde. Se o servidor tem uma novidade, ele não consegue avisar: precisa esperar o próximo pedido. Isso funciona para uma página de produto, mas fica ruim para um chat, um placar ao vivo ou um painel de métricas.

O WebSocket resolve isso com uma conexão que não fecha depois da resposta. Com ela aberta, qualquer um dos lados envia uma mensagem quando quiser, sem um novo pedido. O protocolo está definido na RFC 6455 e a API do navegador é documentada na MDN.

HTTPClienteServidorpedidorespostapedidorespostao servidor sórespondeWebSocketClienteServidorGET Upgrade: websocket101 Switching Protocolsmesma conexãotextotextopingpongClose
O WebSocket começa como HTTP, vira com o 101 e mantém a conexão aberta para mensagens nos dois sentidos.

Como um WebSocket começa?#

Com uma requisição HTTP comum. O cliente pede para trocar de protocolo, e o servidor aceita:

http
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

O status 101 Switching Protocols marca a virada. A partir dali a mesma conexão TCP deixa de carregar HTTP e passa a carregar mensagens WebSocket.

O cabeçalho Sec-WebSocket-Accept prova que o servidor entende o protocolo. Ele é calculado com SHA-1 sobre o valor de Sec-WebSocket-Key somado a um GUID fixo da especificação, em Base64. Assim o cliente não confunde um servidor HTTP qualquer com um servidor WebSocket. Você não faz essa conta à mão: o navegador e as bibliotecas cuidam disso.

Como o início é HTTP, o WebSocket usa as mesmas portas: 80 para ws:// e 443 para wss://.

O que trafega depois do handshake?#

Quadros (frames). Cada mensagem vai em um ou mais quadros, e o primeiro byte diz o tipo:

Tipo Para que serve
Texto Mensagem em UTF-8, em geral JSON
Binário Bytes crus: áudio, imagem, formatos como Protobuf
Ping e pong Checar se o outro lado ainda está vivo
Close Encerrar a conexão com um código e um motivo

Quadros do cliente para o servidor são mascarados com uma chave de 4 bytes, e o servidor precisa desconectar um cliente que mande quadro sem máscara. A máscara não é criptografia. Para criptografar de verdade, use wss://.

Um ping pode partir de qualquer lado, e quem o recebe responde com um pong com o mesmo conteúdo. Os quadros de controle carregam no máximo 125 bytes.

Como uma conexão fecha?#

Um dos lados envia um quadro Close com um código. Os que você mais vai ver:

  • 1000: encerramento normal.
  • 1001: o lado que fechou está saindo (um servidor que está desligando ou um navegador saindo da página).
  • 1006: encerramento anormal. A conexão caiu sem quadro Close, por exemplo por queda de rede ou proxy.
  • 1008: violação de política.
  • 1009: mensagem grande demais.

Como usar um WebSocket em código?#

Este servidor com a biblioteca ws repassa cada mensagem recebida aos outros clientes conectados. Instale com npm install ws e use "type": "module" no package.json:

js
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("fechou", code));
});

No navegador, a API já vem embutida, sem nada para instalar:

js
const socket = new WebSocket("ws://localhost:8080");

socket.addEventListener("open", () => socket.send("oi"));
socket.addEventListener("message", (event) => console.log(event.data));
socket.addEventListener("close", (event) => console.log("fechou", event.code));

O Node.js também tem WebSocket como cliente global (sem a marca de experimental desde a versão 22.4), então o mesmo código funciona em scripts e testes. Use ws:// em desenvolvimento local e wss:// em produção.

O campo socket.readyState informa o estado: 0 conectando (CONNECTING), 1 aberta (OPEN), 2 fechando (CLOSING) e 3 fechada (CLOSED). Confira WebSocket.OPEN antes de enviar, como no exemplo acima.

Quando usar WebSocket?#

Quando as mensagens fluem nos dois sentidos, em tempo real e com frequência:

  • Chat e notificações instantâneas.
  • Jogos e cursores compartilhados em edição colaborativa.
  • Painéis ao vivo, como cotações e métricas.

Para uma API em que o cliente pede e o servidor responde, uma API REST continua sendo a escolha mais simples, fácil de cachear e de testar.

Quando não usar WebSocket?#

Se só o servidor precisa enviar dados, Server-Sent Events bastam. Eles são HTTP comum, com Content-Type: text/event-stream, e o navegador reconecta sozinho. Se o cliente só pergunta de vez em quando, polling a cada poucos segundos é mais simples.

O WebSocket também tem custos:

  • Estado no servidor. Cada cliente mantém uma conexão aberta e memória ocupada. Mil usuários são mil conexões.
  • Sem reconexão automática. Você programa a retomada, de preferência com espera crescente.
  • Sem cabeçalhos personalizados no navegador. O construtor new WebSocket() não aceita cabeçalhos próprios, então a autenticação costuma ir por cookie, por token na URL (que pode vazar em logs) ou por uma primeira mensagem de autenticação.
  • Sem controle de fluxo. Segundo a MDN, a API WebSocket não tem backpressure: se as mensagens chegam mais rápido do que a página processa, a memória enche. A alternativa WebSocketStream resolve isso, mas não é padrão e tem suporte restrito. A MDN também aponta o WebTransport como candidato a substituir o WebSocket em muitos casos, com suporte menor nos navegadores.
  • Proxies cortam conexões ociosas. Um ping periódico mantém o tráfego fluindo.

Como validar quem se conecta?#

Navegadores sempre enviam o cabeçalho Origin. Confira esse valor no servidor para evitar que outro site abra conexões em nome do usuário (Cross-Site WebSocket Hijacking). Clientes que não são navegadores podem forjar o Origin, então ele não substitui a autenticação.

Que termos são vizinhos do WebSocket?#

  • Socket TCP: a conexão de baixo nível. O WebSocket roda sobre uma e adiciona handshake, quadros e mensagens.
  • Webhook: uma chamada HTTP que um serviço faz quando algo acontece. Não mantém conexão aberta.
  • Subprotocolo: um formato combinado dentro do WebSocket, negociado no handshake pelo segundo argumento de new WebSocket(url, protocols).

Para colocar um servidor desses no ar, leia como hospedar um servidor WebSocket. Se ele ficar atrás de um proxy reverso, o proxy precisa repassar o cabeçalho Upgrade. Atrás de um load balancer, cada conexão fica presa a uma instância. E o certificado TLS é o que torna o wss:// possível.

Como fazer isso na Square Cloud#

Um WebSocket precisa de um processo sempre ligado, e é assim que as aplicações rodam na Square Cloud: em containers isolados, sem cold start e sem modo de suspensão. Com o reinício automático ligado (o padrão no deploy pelo painel), a aplicação volta sozinha quando o processo encerra com erro.

A rede da plataforma tem suporte a WebSockets, além de HTTP/3, WAF e mitigação de DDoS, e o proxy não corta conexões longas. Toda aplicação web recebe um subdomínio *.squareweb.app com HTTPS, então o seu cliente abre wss:// direto nele. Se preferir um domínio personalizado, ele está disponível a partir do plano Standard.

Você faz o deploy do servidor Node.js do exemplo acima, ajusta a RAM da aplicação nas configurações e acompanha os logs em tempo real. Os detalhes estão na página de hospedagem de APIs.

Hospedagem de APIs

APIs em containers sempre ligados, sem cold start

Saiba mais

Perguntas frequentes

WebSocket é a mesma coisa que HTTP?

Não. Um WebSocket começa com uma requisição HTTP, mas depois do handshake a conexão deixa de ser HTTP e passa a trocar mensagens do protocolo WebSocket nos dois sentidos.

WebSocket usa qual porta?

Por padrão, a porta 80 para ws:// e a 443 para wss://, as mesmas do HTTP e do HTTPS. Por isso ele costuma passar por firewalls e pode compartilhar a porta do seu servidor web.

Qual a diferença entre WebSocket e Server-Sent Events?

Server-Sent Events só enviam dados do servidor para o cliente. WebSocket envia nos dois sentidos, em texto ou binário, mas exige tratar reconexão e batimento por conta própria.

Devo usar ws:// ou wss://?

Em produção, wss://, que é WebSocket sobre TLS. Páginas carregadas por HTTPS são em geral impedidas de abrir ws:// sem criptografia, e o tráfego aberto pode ser lido no caminho.

O WebSocket reconecta sozinho se a conexão cair?

Não. Quando a conexão fecha, o evento close dispara e nada mais acontece. Quem programa o cliente precisa reconectar, de preferência com espera crescente entre as tentativas.

Próximo artigoCron jobs, workers em segundo plano ou filasO cron roda tarefas no relógio, o worker é um processo que trabalha em segundo plano e a fila guarda jobs até um worker pegá-los. Veja quando usar cada um.