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

Compressão HTTP no Node.js

Atualizado em: 1 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

Compressão HTTP reduz o tamanho das respostas enviadas pela rede. No Node.js, o módulo node:zlib oferece Gzip, Deflate e Brotli por meio de streams. A compressão pode melhorar tempo de transferência de HTML, CSS, JavaScript, JSON e texto, mas consome CPU e pode ser inútil para imagens e vídeos já comprimidos.

A decisão deve considerar tipo de conteúdo, tamanho, suporte do cliente, cache, latência e capacidade de CPU. Em muitas arquiteturas, CDN ou proxy reverso é o melhor lugar para comprimir respostas públicas.

Accept-Encoding

O cliente informa os formatos aceitos no cabeçalho:

Accept-Encoding: br, gzip, deflate

O servidor escolhe um formato e responde:

Content-Encoding: br
Vary: Accept-Encoding

Vary é importante para que caches diferenciem versões comprimidas e sem compressão.

Servidor com Gzip

import { createServer } from 'node:http';
import { createGzip } from 'node:zlib';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';

const server = createServer(async (req, res) => {
  const body = JSON.stringify({ items: createItems() });
  const acceptsGzip = /\bgzip\b/.test(req.headers['accept-encoding'] || '');

  res.setHeader('content-type', 'application/json; charset=utf-8');
  res.setHeader('vary', 'Accept-Encoding');

  if (!acceptsGzip) {
    res.end(body);
    return;
  }

  res.setHeader('content-encoding', 'gzip');

  try {
    await pipeline(
      Readable.from([body]),
      createGzip(),
      res,
    );
  } catch (error) {
    res.destroy(error);
  }
});

server.listen(3000);

Esse exemplo demonstra negociação simples. Uma implementação completa precisa analisar qualidade, formatos e tipos de conteúdo.

Brotli

import { createBrotliCompress } from 'node:zlib';

const brotli = createBrotliCompress();

Brotli costuma produzir arquivos menores para texto, mas níveis altos podem consumir muita CPU. Para conteúdo estático, comprima durante o build. Para respostas dinâmicas, use configuração moderada e benchmark.

Deflate

Deflate é suportado, mas Gzip e Brotli são opções mais comuns na web. Priorize formatos de acordo com compatibilidade e infraestrutura.

Negociação de qualidade

O cliente pode informar pesos:

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

Não escolha apenas procurando uma substring. Use uma biblioteca de negociação bem testada ou implemente parsing conforme o padrão.

Identity

identity significa resposta sem transformação. Um cliente pode rejeitar certos formatos ou até indicar que não aceita identity. Trate os casos corretamente.

Vary

Sem Vary: Accept-Encoding, um cache pode servir uma resposta Brotli a um cliente que não suporta Brotli, ou armazenar várias versões de forma errada.

Content-Length

O tamanho comprimido não é conhecido antes do processamento em streaming. Remova um Content-Length calculado sobre o corpo original. O Node.js pode usar transferência em chunks ou o protocolo adequado.

Não comprima tudo

Evite compressão para:

  • JPEG, PNG, WebP e AVIF;
  • MP4 e áudio comprimido;
  • ZIP, Gzip e outros arquivos compactados;
  • respostas muito pequenas;
  • dados criptografados;
  • streams em que latência por chunk é crítica.

Comprimir conteúdo já comprimido gasta CPU e pode aumentar tamanho.

Limiar mínimo

Defina um tamanho mínimo, por exemplo alguns centenas de bytes ou mais, após benchmark. Cabeçalhos e custo de CPU podem superar o ganho em corpos pequenos.

if (Buffer.byteLength(body) < 1024) {
  res.end(body);
  return;
}

Tipos de conteúdo

Uma allowlist pode incluir:

  • text/html;
  • text/css;
  • application/javascript;
  • application/json;
  • application/xml;
  • image/svg+xml;
  • text/plain.

Normalize o Content-Type e ignore parâmetros como charset ao comparar.

Compressão dinâmica e estática

Conteúdo estático deve ser pré-comprimido durante build ou no CDN:

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

O servidor escolhe o arquivo correspondente sem gastar CPU em cada requisição.

Compressão de JSON

JSON repetitivo comprime bem. Entretanto, serializar objetos grandes e comprimir na thread principal pode aumentar event loop delay. Limite tamanho, pagine e considere cache.

Streams

Use pipeline para arquivos grandes:

await pipeline(
  createReadStream('export.csv'),
  createGzip(),
  res,
);

O pipeline aplica backpressure e propaga erros.

Flush

Aplicações de streaming podem solicitar flush para enviar dados acumulados, mas flush frequente reduz taxa de compressão e aumenta overhead. Server-Sent Events e respostas progressivas exigem testes específicos.

Nível de compressão

Níveis maiores geralmente reduzem mais o tamanho, com custo de CPU e latência. Para Gzip:

import { createGzip, constants } from 'node:zlib';

const gzip = createGzip({
  level: constants.Z_BEST_SPEED,
});

Não presuma que melhor compressão é melhor para usuário. Compare tempo total.

Parâmetros do Brotli

Brotli oferece parâmetros de qualidade e modo. Níveis máximos são adequados para build offline, não necessariamente para respostas dinâmicas.

CPU e thread pool

Operações zlib assíncronas usam recursos do runtime e podem competir com outras tarefas. Muitas compressões simultâneas aumentam filas e memória. Monitore saturação e limite concorrência.

Cache

Cacheie variantes por encoding. Uma chave pode incluir URL normalizada, versão e encoding. Não misture respostas personalizadas de usuários.

ETag

Um ETag calculado sobre a representação sem compressão pode ser fraco ou tratado conforme a estratégia do servidor. Garanta consistência entre variantes e condicionais. Proxies podem alterar a representação.

CDN

CDNs normalmente oferecem compressão automática, cache e Brotli. Evite comprimir no Node.js e novamente no proxy. Verifique headers finais.

Proxy reverso

Nginx, Envoy e ingress controllers podem comprimir. Centralizar reduz CPU da aplicação e padroniza políticas. Ainda assim, respostas internas e streaming podem exigir configuração específica.

Segurança

Compressão de respostas que misturam segredo e entrada controlada pelo usuário pode criar canais laterais de tamanho. Reduza riscos:

  • não refletir segredos em respostas;
  • separar dados sensíveis;
  • usar tokens imprevisíveis;
  • desativar compressão em páginas críticas quando necessário;
  • aplicar políticas de segurança modernas.

Decompression bombs

Ao aceitar conteúdo comprimido, limite tamanho comprimido e descomprimido. Um arquivo pequeno pode expandir para gigabytes.

let decompressedBytes = 0;
transform.on('data', (chunk) => {
  decompressedBytes += chunk.length;
  if (decompressedBytes > MAX_SIZE) {
    transform.destroy(new Error('Conteúdo descomprimido excedeu o limite'));
  }
});

Observabilidade

Meça:

  • bytes antes e depois;
  • razão de compressão;
  • duração;
  • CPU;
  • event loop delay;
  • formato escolhido;
  • respostas abaixo do limiar;
  • erros;
  • cache hit por variante.

Teste de carga

Compare sem compressão, Gzip e Brotli em diferentes níveis. Meça tempo total do cliente, p95, p99, throughput, CPU e banda. Use payloads reais.

Erros comuns

  • comprimir imagens já comprimidas;
  • não enviar Vary;
  • manter Content-Length original;
  • usar Brotli máximo dinamicamente;
  • comprimir respostas minúsculas;
  • ignorar backpressure;
  • comprimir duas vezes;
  • não limitar descompressão;
  • medir somente tamanho e ignorar CPU.

Fluxo recomendado

Prefira CDN ou proxy para conteúdo público, pré-comprima arquivos estáticos e use compressão dinâmica moderada apenas para tipos e tamanhos adequados. Combine com Streams e Backpressure, Event Loop Utilization, CPU Profiling e testes com Autocannon.

Consulte a documentação oficial de zlib e a referência de Accept-Encoding.

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