Todos os artigos
172 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Comparativos

Gzip vs Brotli: qual compressão usar

Gzip vs Brotli: aprenda qual compressão usar, com exemplos práticos, erros comuns e cuidados para aplicar com segurança em produção.

Gzip vs Brotli: qual compressão usar
COVER · Comparativos

Liguei Brotli no nível máximo e derrubei o servidor

Vi um benchmark dizendo que Brotli no nível 11 comprime bem melhor que Gzip, achei que era só trocar uma flag e fui em frente. Liguei brotli on com qualidade 11 para todo o tráfego dinâmico de uma API. Resultado: a latência do p95 dobrou e a CPU do servidor foi para o teto sob carga. O ganho de bytes era real, mas eu estava pagando esse ganho em CPU a cada request, em conteúdo que nem dava para cachear. A lição não foi "Brotli é ruim" — foi que compressão é uma troca entre CPU e banda, e essa troca muda conforme o conteúdo é estático ou dinâmico.

Aqui eu comparo Gzip e Brotli do ponto de vista de quem opera, não de quem só lê benchmark. Como compressão e cache resolvem problemas vizinhos — um reduz o tamanho do que trafega, o outro evita trafegar de novo — vale ler junto o texto sobre cache do navegador com Cache-Control e ETag.

Como compressão HTTP funciona, rápido

O mecanismo é uma negociação. O cliente manda no request um header dizendo o que ele aceita:

Accept-Encoding: gzip, br

O servidor escolhe um algoritmo que entende, comprime a resposta e devolve avisando o que usou:

Content-Encoding: br
Vary: Accept-Encoding

O br é o identificador do Brotli; gzip é o do Gzip. O header Vary: Accept-Encoding é importante e esquecido com frequência: ele diz ao cache (CDN, proxy) que a resposta varia conforme o encoding, para não servir um corpo Brotli a um cliente que só entende Gzip. Esquecer isso entrega bytes ilegíveis para parte dos usuários.

A diferença real entre os dois

Gzip existe desde os anos 90, é universal e rápido. Brotli é mais novo (Google, ~2015), foi desenhado para a web e comprime menos bytes que o Gzip em texto — HTML, CSS, JS, JSON. A diferença típica fica na casa de 15% a 25% menor para assets de web no nível máximo.

O detalhe que muda tudo é a curva de custo. Brotli tem 12 níveis de qualidade (0 a 11). O nível 11 comprime o máximo, mas é dramaticamente mais lento que o Gzip no nível equivalente — pode ser uma ordem de grandeza mais caro em CPU. Os níveis baixos do Brotli, por outro lado, são rápidos e comprimem parecido com o Gzip rápido. Ou seja: "Brotli vs Gzip" não é uma pergunta única. É "Brotli nível alto vs Gzip" e "Brotli nível baixo vs Gzip", e as respostas são opostas.

Estático vs dinâmico: a decisão que importa

Essa é a separação que evita o erro que cometi. O conteúdo se divide em dois mundos e cada um pede uma estratégia.

Estático é tudo que você gera uma vez no build e serve muitas vezes sem mudar: o bundle de JS, o CSS, fontes em formato texto. Para isso a estratégia é precompressão. Você comprime no nível máximo (Brotli 11) durante o build, salva o .br ao lado do arquivo, e o servidor só entrega o arquivo já pronto. O custo de CPU é pago uma vez, offline. A latência em runtime é zero. É o melhor dos dois mundos e não tem desculpa para não fazer.

# nginx servindo arquivos pré-comprimidos sem comprimir em runtime
brotli_static on;
gzip_static on;

Dinâmico é o que muda a cada request: a resposta JSON de uma API, HTML renderizado por usuário. Aqui você não tem como precomprimir — tem que comprimir na hora, dentro da janela do request. A estratégia é o oposto: nível baixo. Brotli 4 ou 5, ou Gzip 5 a 6. Você abre mão de alguns bytes para não estourar a CPU sob carga. Foi exatamente onde eu errei: usei nível 11 em conteúdo dinâmico.

Não comprima o que já está comprimido

Erro silencioso e comum. Imagens (JPEG, PNG, WebP), vídeo (MP4), áudio, arquivos .zip, .gz — todos já estão comprimidos no próprio formato. Passar Gzip ou Brotli por cima deles não reduz tamanho (às vezes aumenta uns bytes) e gasta CPU à toa.

A regra é comprimir só conteúdo baseado em texto. Configure por tipo MIME:

# comprima texto; deixe binário comprimido em paz
gzip_types text/plain text/css application/json
           application/javascript text/xml application/xml
           image/svg+xml;

SVG entra porque é texto (XML). WebP e AVIF ficam de fora porque já são binários comprimidos. Mandar comprimir */* é um anti-padrão que custa CPU sem retorno.

O que realmente configurar

Juntando tudo, o setup que eu recomendo para a maioria dos projetos:

  • Assets estáticos: precomprimidos em build, Brotli 11 e Gzip 9 lado a lado, servidos com brotli_static/gzip_static. Custo zero em runtime, máximo de economia de banda.
  • Respostas dinâmicas: compressão em runtime com Brotli em nível 4–5 (ou Gzip 5–6), só para tipos de texto. Equilíbrio entre CPU e banda.
  • CDN na frente: se você usa Cloudflare, Fastly ou similar, boa parte disso é resolvido na borda — eles negociam encoding, cacheiam por Vary e muitas vezes precomprimem. Confira o que a sua CDN já faz antes de duplicar o trabalho no origin.
  • Limite mínimo: não comprima respostas minúsculas. Abaixo de ~1KB o overhead do encoding não compensa; a maioria dos servidores tem um gzip_min_length para isso.

A CPU economizada no dinâmico costuma valer mais que os bytes a mais — banda é barata, latência sob carga não é. No estático é o contrário: comprima sem dó, porque o custo já foi pago no build.

Perguntas frequentes

Brotli é sempre melhor que Gzip?

Em densidade de compressão para texto, sim, no mesmo nível de esforço. Mas "melhor" depende do custo de CPU. Brotli no nível alto é melhor para estático precomprimido; em conteúdo dinâmico, um Brotli de nível baixo ou até Gzip pode ser a escolha certa porque comprime rápido. Não existe vencedor único — depende de estático vs dinâmico.

Preciso desativar o Gzip se ligar o Brotli?

Não. Mantenha os dois. Nem todo cliente entende Brotli (clientes antigos, alguns proxies), e o Accept-Encoding deixa o cliente escolher. O servidor entrega Brotli para quem aceita e Gzip como fallback. Servir os dois é o comportamento correto.

Por que comprimir imagem não adianta?

Porque JPEG, PNG, WebP e companhia já aplicam compressão específica para imagem internamente. Gzip e Brotli são feitos para texto e não acham redundância nova em dados já comprimidos. O resultado é tamanho praticamente igual com CPU gasta à toa — às vezes o arquivo até cresce alguns bytes por causa do header.

Qual nível de Brotli usar para conteúdo dinâmico?

Algo entre 4 e 5 na prática. É onde a relação compressão/CPU fica saudável para comprimir na janela do request sem castigar a latência. O nível 11 é reservado para precompressão offline de assets estáticos, onde o tempo de CPU não conta contra o usuário.

O que levar disso

Compressão não é uma flag que você liga e esquece — é uma troca consciente entre CPU e banda, e a resposta certa depende de o conteúdo ser estático ou dinâmico. Para estático: precomprima no nível máximo no build e sirva o arquivo pronto. Para dinâmico: nível baixo, comprimindo na hora, só para texto. Não comprima binário já comprimido, não esqueça o Vary: Accept-Encoding, e mantenha Gzip e Brotli convivendo para cobrir todos os clientes. O erro que me custou um p95 dobrado foi tratar tudo como se fosse a mesma coisa.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil