Fazer proxy de conexões WebSocket envolve configurar um servidor intermediário para tratar corretamente o handshake HTTP Upgrade e manter a conexão TCP persistente e full-duplex exigida pela comunicação WebSocket. Essa configuração habilita recursos como balanceamento de carga, terminação SSL, cache e controle de acesso para aplicações em tempo real.
Entendendo o proxy de WebSocket
WebSockets estabelecem uma conexão persistente entre cliente e servidor sobre uma única conexão TCP. Isso difere do HTTP tradicional, que normalmente usa ciclos curtos de requisição/resposta. A transição de HTTP para WebSocket é iniciada pelo mecanismo "Upgrade" do protocolo HTTP/1.1.
O cliente envia uma requisição HTTP GET inicial com cabeçalhos específicos:
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: [nonce codificado em base64]
* Sec-WebSocket-Version: 13
Se o servidor suportar WebSockets, ele responde com o status HTTP 101 Switching Protocols, confirmando o upgrade:
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: [chave de resposta derivada da chave do cliente]
Após esse handshake, a conexão deixa de ser HTTP e passa a ser um socket TCP bruto, sobre o qual são trocados frames WebSocket. O servidor proxy precisa estar configurado para encaminhar corretamente os cabeçalhos Upgrade e Connection e, em seguida, manter a conexão TCP de longa duração sem fazer buffering nem fechá-la prematuramente.
Configuração nos servidores proxy mais comuns
Nginx
O Nginx faz proxy de conexões WebSocket de forma eficiente ao tratar o handshake HTTP Upgrade. O ponto-chave é repassar os cabeçalhos Upgrade e Connection do cliente para o backend e garantir o uso de HTTP/1.1.
http {
upstream websocket_backend {
server backend_server_1:8080;
server backend_server_2:8080;
}
server {
listen 80;
server_name yourdomain.com;
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host; # Importante se o backend depender do cabeçalho host
proxy_read_timeout 86400s; # Ajuste conforme necessário para conexões de longa duração
proxy_send_timeout 86400s;
proxy_buffering off; # Desativa o buffering para dados em tempo real
}
# Outras locations HTTP
location / {
proxy_pass http://other_http_backend;
# ... outras configurações de proxy HTTP
}
}
}
proxy_http_version 1.1;: essencial para o mecanismo do cabeçalhoUpgrade.proxy_set_header Upgrade $http_upgrade;: encaminha o cabeçalhoUpgradedo cliente.proxy_set_header Connection "upgrade";: encaminha o cabeçalhoConnectiondo cliente. O Nginx transforma automaticamenteConnection: upgradeemConnection: keep-aliveem conexões HTTP/1.1 se o valor não for definido explicitamente como "upgrade". Definir explicitamente garante o comportamento correto para WebSockets.proxy_read_timeout/proxy_send_timeout: aumente esses valores padrão para impedir que o Nginx feche conexões WebSocket de longa duração por inatividade.proxy_buffering off;: impede que o Nginx faça buffering das respostas, o que é crítico para a comunicação WebSocket em tempo real garantir latência mínima.
Apache HTTP Server
O Apache faz proxy de conexões WebSocket usando o mod_proxy_wstunnel, que faz parte do mod_proxy. Garanta que mod_proxy, mod_proxy_http e mod_proxy_wstunnel estejam habilitados.
<VirtualHost *:80>
ServerName yourdomain.com
# Habilita os módulos de proxy
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
# Proxy de conexões WebSocket
<Location /ws/>
ProxyPass ws://backend_server:8080/ws/
ProxyPassReverse ws://backend_server:8080/ws/
</Location>
# Proxy de conexões WebSocket seguras (WSS)
<Location /wss/>
ProxyPass wss://backend_server:8443/wss/
ProxyPassReverse wss://backend_server:8443/wss/
</Location>
# Outras locations HTTP
<Location />
ProxyPass http://other_http_backend/
ProxyPassReverse http://other_http_backend/
</Location>
</VirtualHost>
ProxyPass ws://...: o esquemaws://diz aomod_proxy_wstunnelpara tratar o upgrade WebSocket. Para WebSockets seguros, usewss://.ProxyPassReverse: garante a reescrita correta de URLs nos cabeçalhos de resposta.- O
mod_proxydo Apache trata os cabeçalhosUpgradeeConnectionautomaticamente quandows://ouwss://é especificado.
HAProxy
O HAProxy suporta proxy de WebSocket desde a versão 1.5. Ele pode operar em modo http, para inspecionar cabeçalhos, ou em modo tcp, para encaminhamento bruto. Para WebSockets, é comum usar o modo http com regras específicas que detectam a requisição de upgrade.
global
log /dev/log local0 info
maxconn 4000
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms # Aumentado para conexões WebSocket do lado do cliente
timeout server 50000ms # Aumentado para conexões WebSocket do lado do backend
frontend http_frontend
bind *:80
acl is_websocket hdr(Upgrade) -i websocket
use_backend ws_backend if is_websocket
default_backend http_backend
backend ws_backend
mode http
# Esta opção é crucial para WebSockets em modo HTTP; ela manda o HAProxy
# mudar para tunelamento TCP após o upgrade HTTP.
option http-tunnel
server backend_ws_1 backend_server_1:8080 check
server backend_ws_2 backend_server_2:8080 check
backend http_backend
mode http
server backend_http_1 backend_server_3:80 check
acl is_websocket hdr(Upgrade) -i websocket: define uma Access Control List (ACL) que casa com requisições contendo o cabeçalhoUpgrade: websocket(sem diferenciar maiúsculas de minúsculas).use_backend ws_backend if is_websocket: direciona as requisições de upgrade WebSocket para ows_backend.option http-tunnel: isto é crítico. Quando o HAProxy detecta um cabeçalhoUpgradee essa opção está ativa, ele passa do modo de parsing HTTP para um túnel TCP bruto após o handshake HTTP inicial. Isso permite que o protocolo WebSocket opere sem interferência.timeout client/timeout server: esses timeouts devem ser aumentados de forma significativa para backends WebSocket, evitando que o HAProxy encerre conexões de longa duração.
Resolução de problemas comuns
Queda de conexão ou timeout
- Timeouts do proxy: a causa mais comum. Garanta que
proxy_read_timeout,proxy_send_timeout(Nginx),timeout client,timeout server(HAProxy) ou equivalentes em outros proxies estejam altos o bastante (por exemplo, 24 horas ou mais) para conexões WebSocket. - Conexões ociosas: alguns proxies ou dispositivos de rede podem fechar conexões TCP ociosas de forma agressiva. Se o protocolo WebSocket tiver mecanismo de keep-alive (por exemplo, frames ping/pong), garanta que ele esteja configurado no cliente e no servidor para enviar pings periódicos.
- Inatividade no balanceador de carga: se houver um balanceador de carga à frente do proxy, ele também pode ter seus próprios timeouts de inatividade. Verifique essas configurações.
HTTP 400 Bad Request (cabeçalho Upgrade ausente ou incorreto)
- Requisição do cliente: verifique se o cliente está enviando corretamente os cabeçalhos
Upgrade: websocketeConnection: Upgrade. - Configuração do proxy: garanta que o proxy esteja configurado para encaminhar esses cabeçalhos ao backend.
- Nginx: confira
proxy_set_header Upgrade $http_upgrade;eproxy_set_header Connection "upgrade";. - Apache: garanta que o
mod_proxy_wstunnelesteja habilitado e que oProxyPassusews://ouwss://. - HAProxy: confirme que a
acl is_websocketidentifica corretamente o cabeçalhoUpgradee que ouse_backenda direciona para um backend comoption http-tunnel.
- Nginx: confira
HTTP 502 Bad Gateway
- Problemas no servidor backend: o proxy recebeu uma resposta inválida do backend.
- O servidor WebSocket de backend está rodando e escutando na porta esperada?
- O backend está configurado corretamente para tratar upgrades WebSocket?
- Verifique os logs do servidor backend em busca de erros.
- Conectividade de rede: verifique se o proxy consegue alcançar o servidor backend na porta especificada.
- Buffering do proxy: embora
proxy_buffering offseja geralmente bom para WebSockets, volume excessivo de dados ou configurações incorretas podem às vezes causar 502 se o proxy tiver dificuldade em lidar com o fluxo.
Considerações de segurança (terminação SSL/TLS)
- WSS (WebSockets seguros): para conexões WebSocket seguras (wss://), o proxy precisa fazer a terminação SSL/TLS.
- O proxy escuta na porta 443 (ou outra porta segura), executa o handshake TLS e então repassa o tráfego WebSocket descriptografado ao backend (muitas vezes internamente por
ws://ouhttp://puros). - Exemplo de Nginx para WSS:
```nginx
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/yourdomain.crt;
ssl_certificate_key /etc/nginx/ssl/yourdomain.key;location /ws/ { proxy_pass http://websocket_backend; # O backend pode ser HTTP puro proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 86400s; proxy_send_timeout 86400s; proxy_buffering off; }}
`` * **Cabeçalhos Origin:** clientes WebSocket enviam um cabeçalhoOrigin`. Servidores de backend frequentemente validam esse cabeçalho por motivos de segurança. Garanta que seu proxy não esteja removendo nem alterando esse cabeçalho, caso o backend dependa dele.
- O proxy escuta na porta 443 (ou outra porta segura), executa o handshake TLS e então repassa o tráfego WebSocket descriptografado ao backend (muitas vezes internamente por
Limites de buffer do proxy
- Nginx:
proxy_buffering off;é crucial. Se não for definido, o Nginx pode fazer buffering de mensagens WebSocket grandes, introduzindo latência ou causando timeouts. - HAProxy:
option http-tunnelresolve isso ao mudar para TCP bruto.
Comparação de servidores proxy para WebSockets
| Recurso | Nginx | Apache HTTP Server (mod_proxy_wstunnel) | HAProxy |
|---|---|---|---|
| Configuração | Encaminhamento manual de cabeçalhos | Diretivas específicas ws:// / wss:// |
ACLs e option http-tunnel |
| Complexidade | Moderada | Moderada | Moderada a alta (para recursos avançados) |
| Desempenho | Alto (orientado a eventos) | Moderado (baseado em processos/threads) | Alto (orientado a eventos) |
| Terminação SSL/TLS | Excelente | Boa | Excelente |
| Balanceamento de carga | Round-robin, IP hash, least_conn | Round-robin | Extenso (muitos algoritmos, health checks) |
| Sticky sessions | IP hash (básico), módulos de terceiros | Limitado | Baseado em cookie, IP de origem |
| Controle de buffering | proxy_buffering off |
Tratado pelo mod_proxy_wstunnel |
option http-tunnel desativa o buffering HTTP |
Pontos-chave a considerar
- Sticky sessions: em algumas aplicações WebSocket, o cliente precisa permanecer conectado ao mesmo servidor backend durante toda a sessão (por exemplo, se houver estado mantido no servidor).
- Nginx: use
ip_hashno blocoupstream. - HAProxy: use as diretivas
balance sourceoucookie. - Sempre que possível, projete sua aplicação de backend para escalar horizontalmente sem exigir sticky sessions, pois isso simplifica o balanceamento de carga.
- Nginx: use
- Health checks: configure health checks robustos para seus servidores WebSocket de backend dentro do proxy. Isso garante que apenas servidores saudáveis recebam requisições de upgrade WebSocket.
- Logging: configure logs abrangentes no proxy e no backend. Problemas de conexão WebSocket podem ser transitórios, e logs detalhados são essenciais para o diagnóstico.
- HTTP/2 e WebSockets: embora os WebSockets normalmente usem HTTP/1.1 para o handshake de upgrade, eles podem coexistir com HTTP/2. No entanto, o protocolo WebSocket em si não roda diretamente sobre frames HTTP/2. Proxies que recebem HTTP/2 dos clientes costumam fazer downgrade para HTTP/1.1 no upgrade WebSocket até o backend.
