Proxy de WebSocket com Nginx

    WebSocket exige configuração especial no Nginx — diferente de HTTP, o protocolo é persistente e requer o mecanismo de upgrade de conexão. Sem a configuração correta, o Nginx fecha a conexão WebSocket após um timeout curto ou retorna 502. Com os headers de upgrade e os timeouts corretos, o proxy funciona transparentemente.

    Configuração básica de proxy WebSocket

    Headers essenciais para o upgrade de protocolo HTTP → WebSocket:

    nginx
    # /etc/nginx/conf.d/websocket.conf
    
    # Map para suportar WebSocket upgrade
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }
    
    server {
        listen 443 ssl http2;
        server_name api.seudominio.com.br;
    
        ssl_certificate     /etc/letsencrypt/live/api.seudominio.com.br/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/api.seudominio.com.br/privkey.pem;
    
        # Proxy para API REST normal
        location /api/ {
            proxy_pass http://127.0.0.1:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    
        # Proxy para WebSocket
        location /ws/ {
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
    
            # Headers de upgrade — OBRIGATÓRIOS para WebSocket
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
    
            # Headers padrão
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # Timeouts para conexões persistentes
            proxy_read_timeout 3600s;    # 1 hora de timeout de inatividade
            proxy_send_timeout 3600s;
        }
    }
    Dica
    O map $http_upgrade $connection_upgrade define o valor do header Connection: se a requisição vem com Upgrade, retorna "upgrade"; caso contrário, retorna "close". Isso garante que requisições HTTP normais não ficam com Connection: upgrade.

    Socket.IO com Nginx

    Socket.IO tenta WebSocket primeiro e faz fallback para long-polling — configuração específica:

    nginx
    # Socket.IO usa o path /socket.io/ por padrão
    # e precisa suportar tanto HTTP (polling) quanto WebSocket
    
    server {
        listen 443 ssl http2;
        server_name chat.seudominio.com.br;
    
        # Proxy para Socket.IO (websocket + long-polling)
        location /socket.io/ {
            proxy_pass http://127.0.0.1:3001;
            proxy_http_version 1.1;
    
            # Upgrade para WebSocket
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
    
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # Sem buffering para long-polling funcionar
            proxy_buffering off;
    
            # Timeout longo para WebSocket
            proxy_read_timeout 86400s;   # 24 horas
    
            # Cache off para SSE/polling
            proxy_cache off;
        }
    
        location / {
            proxy_pass http://127.0.0.1:3001;
            proxy_set_header Host $host;
        }
    }

    Load balancing de WebSocket com sticky sessions

    WebSocket precisa de sticky sessions no load balancer — a mesma conexão deve ir sempre ao mesmo backend:

    nginx
    # Para WebSocket com múltiplos backends:
    # Conexões WebSocket são persistentes — uma vez estabelecida,
    # deve continuar no mesmo backend pelo ciclo de vida da conexão
    
    upstream ws_cluster {
        # IP hash: garante que o mesmo IP vai ao mesmo servidor
        ip_hash;
    
        server 10.0.0.10:3000;
        server 10.0.0.11:3000;
        server 10.0.0.12:3000;
    
        keepalive 100;    # manter conexões abertas entre Nginx e backends
    }
    
    server {
        listen 443 ssl http2;
        server_name ws.seudominio.com.br;
    
        location / {
            proxy_pass http://ws_cluster;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_set_header Host $host;
            proxy_read_timeout 3600s;
        }
    }
    Dica
    Para Socket.IO com múltiplos servidores e sticky sessions por cookie (mais confiável que IP hash): use o parâmetro sticky do Nginx Plus ou implemente sticky sessions via header Set-Cookie no backend para roteamento consistente.

    Configurar Server-Sent Events (SSE)

    SSE é mais simples que WebSocket para streaming unidirecional e precisa de configuração específica:

    nginx
    # SSE usa HTTP regular mas com resposta infinita
    # Requer desabilitar buffering e keepalive com timeout longo
    
    server {
        location /api/events {
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
    
            # OBRIGATÓRIO para SSE funcionar:
            proxy_set_header Connection "";        # sem Connection: close
            proxy_buffering off;                   # enviar bytes imediatamente
            proxy_cache off;                       # sem cache
    
            # Timeout longo (SSE pode durar horas)
            proxy_read_timeout 3600s;
    
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # Headers CORS para SSE cross-origin:
            add_header Access-Control-Allow-Origin $http_origin always;
            add_header Access-Control-Allow-Credentials true always;
            add_header Cache-Control no-cache always;
            add_header X-Accel-Buffering no;       # desabilitar buffering do Nginx
        }
    }

    Diagnosticar problemas com WebSocket

    Como identificar por que conexões WebSocket estão falhando:

    bash
    # Testar WebSocket via wscat:
    npm install -g wscat
    wscat -c wss://api.seudominio.com.br/ws/
    
    # Verificar logs do Nginx em tempo real:
    sudo tail -f /var/log/nginx/error.log | grep -i "websocket\|upgrade\|101"
    
    # Erros comuns e soluções:
    
    # 1. "502 Bad Gateway" ao conectar
    # → Backend não está rodando na porta configurada
    # → Verificar: curl http://127.0.0.1:3000/ws/
    
    # 2. Conexão cai após 60 segundos
    # → proxy_read_timeout muito curto
    # → Aumentar para 3600s e adicionar heartbeat no cliente
    
    # 3. "426 Upgrade Required"
    # → Faltam headers Upgrade e Connection no proxy
    # → Verificar configuração do location
    
    # 4. "mixed content" no browser
    # → Tentando conectar ws:// em página https://
    # → Usar wss:// (WebSocket Secure) na aplicação
    
    # Verificar handshake WebSocket com curl:
    curl -I -H "Upgrade: websocket" -H "Connection: Upgrade" \
      -H "Sec-WebSocket-Key: dGhlIHNhbXBsZQ==" \
      -H "Sec-WebSocket-Version: 13" \
      https://api.seudominio.com.br/ws/
    # Deve retornar: HTTP/1.1 101 Switching Protocols

    $ runstack deploy --plan starter

    Não quer configurar manualmente?

    Não quer configurar manualmente? Implante o VPS em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.

    Perguntas frequentes