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

Compressão HTTP no Node.js

Atualizado em: 22 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

A compressão HTTP no Node.js reduz o tamanho de respostas de texto, JSON, CSS, JavaScript e outros formatos antes do envio ao cliente. Menos bytes significam downloads mais rápidos, menor consumo de banda e melhor aproveitamento de conexões lentas. Porém, comprimir tudo indiscriminadamente pode aumentar CPU, atrasar respostas pequenas e causar problemas de cache ou segurança.

O cliente informa algoritmos aceitos por meio de Accept-Encoding. O servidor escolhe uma codificação, como gzip, Brotli ou deflate, e responde com Content-Encoding. A representação comprimida precisa ser tratada como uma variante, normalmente com Vary: Accept-Encoding.

Neste guia, você aprenderá negociação de conteúdo, gzip, Brotli, streams, limites, cache, ETag, proxies, arquivos estáticos, segurança, testes e observabilidade.

Como funciona a compressão HTTP?

O cliente pode enviar:

Accept-Encoding: br, gzip, deflate

O servidor escolhe uma opção:

Content-Encoding: br
Vary: Accept-Encoding

O RFC 9110 sobre semântica HTTP descreve negociação e Content-Encoding. A documentação oficial do módulo Zlib apresenta gzip, deflate e Brotli no Node.js.

Para fundamentos, consulte Zlib no Node.js. Para respostas em fluxo, veja Streams no Node.js.

Quando comprimir?

Compressão costuma funcionar bem em:

  • JSON;
  • HTML;
  • CSS;
  • JavaScript;
  • XML;
  • SVG;
  • texto;
  • logs e relatórios textuais.

Arquivos como JPEG, PNG, MP4, ZIP e PDF frequentemente já possuem compressão. Recomprimi-los consome CPU com pouco ganho.

Tamanho mínimo

Respostas pequenas podem aumentar de tamanho ou sofrer latência desnecessária. Defina um threshold:

const MINIMUM_COMPRESSION_SIZE = 1024;

O valor ideal depende do tráfego, CPU e algoritmo. Meça com dados reais.

Gzip com node:zlib

const { createGzip } = require('node:zlib');
const { pipeline } = require('node:stream');

function sendGzip(req, res, source) {
  res.setHeader('Content-Encoding', 'gzip');
  res.setHeader('Vary', 'Accept-Encoding');

  pipeline(
    source,
    createGzip(),
    res,
    error => {
      if (error) {
        res.destroy(error);
      }
    }
  );
}

Pipeline propaga erros e encerra streams adequadamente.

Brotli

const {
  createBrotliCompress,
  constants
} = require('node:zlib');

const compressor = createBrotliCompress({
  params: {
    [constants.BROTLI_PARAM_QUALITY]: 4
  }
});

Brotli pode gerar arquivos menores que gzip em conteúdo textual, mas níveis altos consomem mais CPU. Para respostas dinâmicas, use qualidade moderada.

Escolhendo o algoritmo

function selectEncoding(header = '') {
  const accepted = header
    .split(',')
    .map(item => item.trim().split(';')[0]);

  if (accepted.includes('br')) return 'br';
  if (accepted.includes('gzip')) return 'gzip';
  return 'identity';
}

Esse parser simplificado não trata corretamente valores de qualidade q. Em produção, use uma biblioteca testada ou implemente a gramática completa.

Valores q

Accept-Encoding: gzip;q=1.0, br;q=0.8, identity;q=0.5

O cliente expressa preferência. q=0 significa não aceitar aquela codificação.

identity

identity significa sem compressão. Se o cliente rejeita todas as opções e identity possui q=0, o servidor pode retornar 406 Not Acceptable.

Middleware de compressão

Frameworks costumam oferecer middleware que negocia algoritmos e filtra tipos. Mesmo assim, configure:

  • threshold;
  • níveis;
  • tipos permitidos;
  • exclusão de streams sensíveis;
  • Vary;
  • monitoramento de CPU.

Exemplo com Express

const compression = require('compression');

app.use(compression({
  threshold: 1024,
  level: 6
}));

Não use nível máximo por padrão. O ganho adicional costuma ser pequeno em relação ao custo.

Resposta JSON

JSON grande comprime bem por repetir nomes de propriedades. Porém, antes de comprimir, reduza o payload:

  • remova campos desnecessários;
  • use paginação;
  • evite objetos duplicados;
  • não envie logs ou debug;
  • selecione apenas colunas necessárias.

Veja Paginação em APIs Node.js.

Streaming

Compressão em stream evita carregar o corpo inteiro na memória:

await pipeline(
  fileStream,
  createGzip(),
  response
);

O compressor mantém buffers internos e respeita backpressure.

Flush

Em Server-Sent Events ou respostas incrementais, buffers de compressão podem atrasar mensagens. Talvez seja necessário fazer flush ou desabilitar compressão.

Consulte Server-Sent Events com Node.js.

SSE e compressão

Eventos pequenos e frequentes podem ficar presos no compressor. Para baixa latência, envie Cache-Control: no-cache e considere excluir text/event-stream do middleware.

WebSocket

WebSocket possui extensão própria de compressão, como permessage-deflate. A configuração HTTP comum não se aplica depois do upgrade.

Arquivos estáticos pré-comprimidos

Durante o build, gere:

app.js
app.js.gz
app.js.br

O servidor ou CDN seleciona a variante sem gastar CPU em cada requisição.

Assets com hash

Combine pré-compressão com nomes imutáveis:

app.a8f391c2.js

Isso permite cache longo e evita recompressão.

Content-Length

Ao comprimir dinamicamente, o tamanho final geralmente não é conhecido antes do término. Remova Content-Length do corpo original e use transferência em chunks ou deixe o runtime definir.

Vary: Accept-Encoding

Sem Vary, uma CDN pode entregar gzip a cliente que não aceita ou armazenar a variante sem compressão para todos.

res.setHeader('Vary', 'Accept-Encoding');

Ao adicionar Vary, preserve valores anteriores, como Origin.

ETag

Uma resposta gzip possui bytes diferentes da versão Brotli. Um ETag forte pode variar por codificação. Alternativas:

  • ETag específico por variante;
  • ETag fraco baseado no conteúdo semântico;
  • geração feita pelo proxy;
  • checksum do arquivo pré-comprimido.

Veja ETag e Cache HTTP no Node.js.

Cache-Control

Compressão não define validade. Continue configurando Cache-Control conforme o conteúdo.

CDN e proxy reverso

Muitas arquiteturas comprimem no Nginx, CDN ou load balancer. Isso reduz CPU do Node.js e centraliza a política.

Evite compressão dupla. Se o upstream já envia Content-Encoding, o proxy não deve comprimir novamente.

HTTP/2 e HTTP/3

Protocolos modernos comprimem headers, não o corpo automaticamente. Gzip ou Brotli continuam úteis para conteúdo.

Consulte HTTP/2 no Node.js.

CPU e thread pool

Operações assíncronas de zlib podem usar o thread pool da libuv. Compressão intensa compete com filesystem, DNS e criptografia que utilizam o mesmo pool.

Monitorando saturação

Observe:

  • CPU;
  • event loop delay;
  • latência p95 e p99;
  • fila do thread pool;
  • tempo de compressão;
  • bytes antes e depois;
  • algoritmo selecionado.

Veja Performance Hooks no Node.js.

Compressão adaptativa

Sob alta CPU, a aplicação pode reduzir qualidade ou deixar o proxy assumir. Evite mudanças imprevisíveis sem métricas e testes.

Nível gzip

Níveis maiores gastam mais CPU. Para respostas dinâmicas, nível intermediário costuma equilibrar custo e tamanho. Para assets no build, níveis maiores são aceitáveis.

Brotli quality

Qualidades altas de Brotli são caras. Use-as apenas na pré-compressão de arquivos estáticos.

Tipos MIME

Comprima apenas tipos conhecidos. Não confie em extensão fornecida pelo usuário. Use o Content-Type real da resposta.

BREACH e dados sensíveis

Compressão de respostas que misturam segredo e entrada controlada pelo atacante pode permitir ataques de canal lateral. Proteções:

  • não refletir entrada próxima a tokens;
  • usar tokens por requisição;
  • desabilitar compressão em respostas sensíveis;
  • aplicar SameSite e CSRF;
  • evitar segredos no corpo.

Respostas autenticadas

Dados privados podem ser comprimidos, mas avalie riscos e configure cache como private ou no-store.

Bombas de descompressão

Ao receber conteúdo comprimido, limite o tamanho descompactado. Um corpo pequeno pode expandir para gigabytes.

Uploads comprimidos

Não aceite Content-Encoding arbitrário sem parser seguro. Defina:

  • tamanho comprimido;
  • tamanho descomprimido;
  • tempo máximo;
  • algoritmos aceitos;
  • proporção máxima.

Erros de stream

Se a compressão falha após headers enviados, não é possível trocar para JSON de erro. Destrua a conexão e registre o incidente.

AbortController

Quando o cliente desconecta, cancele a origem e feche o compressor. Veja AbortController no Node.js.

Testes

Cubra:

  • cliente com Brotli;
  • cliente apenas gzip;
  • identity;
  • q=0;
  • resposta abaixo do threshold;
  • tipo já comprimido;
  • Vary;
  • ETag por variante;
  • stream grande;
  • cliente desconectado.

Teste gzip

test('comprime JSON com gzip', async () => {
  const response = await request('/api/report', {
    headers: {
      'accept-encoding': 'gzip'
    }
  });

  assert.equal(
    response.headers['content-encoding'],
    'gzip'
  );
});

Benchmark

Compare:

  • sem compressão;
  • gzip em níveis diferentes;
  • Brotli em qualidades moderadas;
  • compressão no proxy;
  • assets pré-comprimidos.

Meça CPU e latência, não apenas tamanho.

Erros comuns

  • Comprimir tudo: CPU aumenta sem ganho.
  • Usar nível máximo: respostas dinâmicas ficam lentas.
  • Esquecer Vary: cache entrega variante errada.
  • Manter Content-Length original: resposta fica corrompida.
  • Comprimir SSE: eventos atrasam.
  • Dupla compressão: proxy e app processam o mesmo corpo.
  • Ignorar dados sensíveis: canais laterais se tornam possíveis.

Boas práticas

  • Defina threshold.
  • Filtre tipos MIME.
  • Prefira Brotli ou gzip conforme cliente.
  • Use qualidade moderada em conteúdo dinâmico.
  • Pré-comprima assets.
  • Configure Vary.
  • Alinhe ETags.
  • Monitore CPU e taxa de compressão.
  • Desabilite em streams de baixa latência.
  • Limite descompressão de entrada.

Conclusão

A compressão HTTP no Node.js reduz o volume de respostas e melhora desempenho em redes, principalmente para JSON e texto. Gzip possui ampla compatibilidade, enquanto Brotli pode entregar tamanhos menores.

A melhor estratégia combina threshold, tipos adequados, níveis moderados, Vary e cache correto. Para assets, pré-compressão no build ou CDN economiza CPU. Com benchmarks e proteção para dados sensíveis, a compressão melhora capacidade sem criar um novo gargalo no servidor.

Os 10 Melhores Cursos de Programação de 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