Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

HTTP Keep-Alive no Node.js

Atualizado em: 1 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

HTTP Keep-Alive permite reutilizar conexões TCP para várias requisições. Em clientes Node.js, isso reduz handshakes, criação de sockets e latência, especialmente quando a aplicação chama repetidamente a mesma API. Em servidores, keep-alive mantém conexões abertas por um período para que o cliente envie novas requisições sem reconectar.

A reutilização melhora eficiência, mas exige limites, timeouts e encerramento correto. Configurações incompatíveis entre cliente, proxy e servidor podem causar resets, sockets ociosos demais ou falhas intermitentes.

Por que reutilizar conexões

Cada nova conexão pode envolver:

  • resolução DNS;
  • handshake TCP;
  • handshake TLS;
  • negociação de protocolo;
  • alocação de socket;
  • controle no balanceador.

Quando dezenas de chamadas usam o mesmo destino, manter sockets disponíveis reduz esse custo e evita explosão de conexões de curta duração.

HTTP Agent

O módulo node:http usa agentes para gerenciar sockets. Um agente com keep-alive pode ser criado assim:

import { Agent, request } from 'node:http';

const agent = new Agent({
  keepAlive: true,
  maxSockets: 50,
  maxFreeSockets: 10,
  timeout: 30_000,
});

function getJson(options) {
  return new Promise((resolve, reject) => {
    const req = request({ ...options, agent }, (res) => {
      let body = '';
      res.setEncoding('utf8');
      res.on('data', (chunk) => body += chunk);
      res.on('end', () => resolve(JSON.parse(body)));
    });

    req.on('error', reject);
    req.end();
  });
}

Para HTTPS, use node:https.Agent. Não reutilize um agente HTTP em chamadas HTTPS.

maxSockets

maxSockets limita sockets simultâneos por origem conforme a estratégia do agente. Um valor muito alto pode sobrecarregar o serviço remoto e o NAT; um valor muito baixo cria fila e aumenta latência.

Dimensione com base em:

  • concorrência da aplicação;
  • tempo médio da dependência;
  • limites do serviço remoto;
  • quantidade de réplicas;
  • capacidade de portas efêmeras;
  • SLO de latência.

maxFreeSockets

Esse valor controla quantos sockets ociosos permanecem no pool. Poucos sockets podem exigir reconexões durante rajadas; muitos sockets consomem recursos e podem ser encerrados pelo servidor remoto antes da próxima utilização.

Timeouts diferentes

Não existe um único timeout. Considere:

  • timeout para conectar;
  • timeout total da operação;
  • timeout de inatividade do socket;
  • keep-alive timeout do servidor;
  • timeout do proxy;
  • deadline da requisição de usuário.

O cliente deve abandonar a chamada antes que o orçamento total expire.

Servidor Node.js

import { createServer } from 'node:http';

const server = createServer((req, res) => {
  res.writeHead(200, {
    'content-type': 'application/json; charset=utf-8',
  });
  res.end(JSON.stringify({ status: 'ok' }));
});

server.keepAliveTimeout = 5_000;
server.headersTimeout = 6_000;
server.requestTimeout = 30_000;

server.listen(3000);

Mantenha headersTimeout maior que o keep-alive timeout para reduzir corridas. Os valores ideais dependem da versão do Node.js, do proxy e do tráfego.

Proxy e balanceador

O caminho pode envolver cliente, CDN, load balancer, ingress e servidor. Cada camada possui seu próprio timeout. Se o cliente reutiliza uma conexão que o proxy acabou de fechar, pode receber ECONNRESET.

Documente os valores e mantenha uma margem entre eles. Em geral, o cliente deve descartar sockets antes do servidor remoto ou proxy.

Conexões HTTPS

import { Agent } from 'node:https';

const httpsAgent = new Agent({
  keepAlive: true,
  maxSockets: 100,
  maxFreeSockets: 20,
});

A reutilização economiza handshakes TLS. Não desative validação de certificado para evitar erros de ambiente. Configure CAs corretamente.

Fetch e Undici

O Fetch do Node.js usa infraestrutura baseada em Undici. Para controle avançado, configure um dispatcher ou pool compatível. Evite criar um novo dispatcher por requisição.

Fila do agente

Quando todos os sockets estão ocupados, novas requisições aguardam. Uma fila ilimitada pode transformar sobrecarga em latência e memória. Aplique:

  • limite de concorrência;
  • timeout na fila;
  • circuit breaker;
  • rate limit;
  • fallback;
  • deadline.

Consumir a resposta

Para que o socket volte ao pool, consuma ou descarte corretamente o corpo da resposta. Abandonar uma resposta no meio pode impedir reutilização.

res.resume();
res.on('end', () => {
  // resposta descartada e socket liberado
});

Quando o corpo importa, use streams e limites de tamanho.

AbortController

Cancelamento deve destruir ou liberar os recursos associados. Em APIs compatíveis, passe um AbortSignal. Em http.request, também é possível usar signal no objeto de opções.

const signal = AbortSignal.timeout(5_000);

const req = request({
  hostname: 'api.internal',
  path: '/items',
  agent,
  signal,
});

Retries

Uma conexão reutilizada pode ser fechada pelo remoto entre o empréstimo e a escrita. Uma tentativa de leitura idempotente pode ser repetida com backoff. Não repita automaticamente pagamentos, criação de pedidos ou outras escritas sem idempotency key.

Portas efêmeras

Sem keep-alive, milhares de conexões de curta duração podem levar a muitos sockets em TIME_WAIT e esgotar portas no host ou NAT. Reutilização reduz churn, mas pools excessivos também podem alcançar limites.

DNS e troca de IP

Uma conexão keep-alive continua ligada ao IP resolvido quando foi criada. Mudanças de DNS não movem sockets existentes. Defina vida útil razoável e reciclagem para serviços que alteram endereços com frequência.

Load balancing

Conexões persistentes podem concentrar tráfego em alguns backends. O balanceador distribui conexões, não necessariamente cada requisição. Muitas conexões moderadas costumam distribuir melhor que poucas conexões enormes, mas aumentam custo.

Graceful shutdown do cliente

Ao encerrar a aplicação, destrua o agente depois de concluir chamadas:

await drainRequests();
agent.destroy();

Destruir cedo interrompe requisições em andamento.

Graceful shutdown do servidor

Use server.close(), pare readiness e feche conexões ociosas. Conexões keep-alive podem prolongar a drenagem; aplique prazo máximo.

Observabilidade

Monitore:

  • sockets ativos;
  • sockets livres;
  • requisições aguardando;
  • conexões criadas;
  • reutilização;
  • ECONNRESET;
  • ETIMEDOUT;
  • latência de conexão e total;
  • handshakes TLS;
  • erros por origem.

Não use hostname dinâmico ou URL completa como label sem normalização.

Teste de carga

Compare keep-alive ativado e desativado com a mesma carga. Meça throughput, p95, p99, conexões por segundo, CPU, sockets, portas e erros. Execute o gerador em outra máquina quando possível.

Erros comuns

  • criar Agent por requisição;
  • usar maxSockets ilimitado;
  • não consumir o corpo;
  • configurar timeout incompatível com o proxy;
  • não destruir agentes no shutdown;
  • repetir escrita após ECONNRESET;
  • manter sockets ociosos por tempo excessivo;
  • ignorar concentração no load balancer;
  • otimizar sem medir fila do pool.

Fluxo recomendado

Crie um agente por origem ou classe de dependência, reutilize-o, limite sockets, consuma respostas e alinhe timeouts com a infraestrutura. Combine com AbortController, Circuit Breaker, Graceful Shutdown e testes com Autocannon.

Consulte a documentação oficial de HTTP Agent e a referência oficial de HTTPS Agent.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita