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.
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
Varye 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_lengthpara 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.
- 01 Nubank Croma: o que vem no plano, quanto custa e pra quem realmente compensa O Nubank lançou o Croma, um plano de média renda entre o cartão gratuito e o Ultravioleta. Veja o que está incluído, o cashback real, os R$ 39 de mensalidade (e como zerar) e faça a conta antes de aderir.
- 02 JSON vs XML: qual formato escolher em cada projeto Comparação direta entre JSON e XML: verbosidade, parsing, schema, namespaces e os casos reais onde XML ainda é a escolha certa.