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
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.
Como um WebSocket começa?#
Com uma requisição HTTP comum. O cliente pede para trocar de protocolo, e o servidor aceita:
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=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:
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:
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
WebSocketnão tem backpressure: se as mensagens chegam mais rápido do que a página processa, a memória enche. A alternativaWebSocketStreamresolve 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
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.
Mais em APIs
Ver todos os 15 artigos- Como hospedar uma APIHospedar uma API é rodar o servidor HTTP 24 horas num lugar com IP público e HTTPS. Veja o passo a passo com Node.js e Python, testes e erros comuns.
- Como hospedar uma aplicação Node.jsPara hospedar uma aplicação Node.js, prepare o package.json, leia porta e segredos do ambiente, escolha uma versão LTS e rode com node. Veja o passo a passo.
- Como hospedar FastAPI, Flask ou DjangoPara hospedar FastAPI, Flask ou Django, rode a aplicação com um servidor de produção (Uvicorn ou Gunicorn) em 0.0.0.0. Veja comandos, ajustes do Django e erros.
- Como hospedar um script ou API em PythonPara hospedar um script ou API em Python, fixe a versão, isole dependências em um venv, leia segredos do ambiente e trate o SIGTERM. Passo a passo com código.