Compressão no Nginx: gzip e brotli em nível conceitual e configuração segura

Capítulo 10

Tempo estimado de leitura: 9 minutos

+ Exercício

O que é compressão HTTP e por que ela importa

Compressão HTTP é o processo de reduzir o tamanho do corpo (body) de uma resposta antes de enviá-la ao cliente. Em vez de transferir o conteúdo “cru”, o servidor envia uma versão comprimida e indica isso via header Content-Encoding (por exemplo, gzip ou br). O navegador (ou cliente HTTP) descomprime automaticamente ao receber.

O objetivo é diminuir bytes transferidos e, muitas vezes, melhorar o tempo de carregamento percebido, especialmente em conexões lentas ou com alta latência. O custo é CPU no servidor (para comprimir) e no cliente (para descomprimir), além de alguns cuidados de compatibilidade e cache.

Quando compressão ajuda (e quando pode atrapalhar)

Ajuda principalmente em conteúdo textual

Conteúdos textuais têm muita redundância e costumam comprimir muito bem. Exemplos típicos:

  • text/html, text/css, application/javascript
  • application/json, application/xml, image/svg+xml
  • Fontes em formato texto (menos comum hoje), manifests, etc.

Em páginas e APIs, a redução pode ser grande (às vezes 60–90%), o que reduz tempo de download e custo de banda.

Pode atrapalhar em conteúdo binário já comprimido

Muitos formatos binários já são comprimidos internamente. Tentar comprimir de novo costuma trazer ganho mínimo e pode piorar:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Imagens: jpg, png, webp, avif
  • Vídeos: mp4, webm
  • Arquivos compactados: zip, gz, rar, 7z
  • PDF (frequentemente já comprimido)

Nesses casos, você gasta CPU para quase nenhum benefício e pode aumentar latência sob carga. Por isso, a configuração segura foca em tipos textuais.

CPU, latência e tamanho de resposta

  • Respostas pequenas: o ganho pode ser irrelevante; o overhead de compressão pode não compensar.
  • Respostas grandes: o ganho tende a ser significativo, mas o custo de CPU também aumenta.
  • Servidores com CPU limitada: compressão agressiva pode virar gargalo.

Compressão e cache: por que o header Vary importa

Clientes enviam Accept-Encoding para dizer quais compressões suportam (ex.: gzip, br). Se você usa cache (CDN, proxy, cache intermediário), é importante que ele diferencie versões comprimidas e não comprimidas. Para isso, a resposta deve incluir:

  • Vary: Accept-Encoding

Sem isso, um cache pode servir uma resposta gzip para um cliente que não pediu gzip (ou o contrário), causando comportamento incorreto.

Cuidado com “double-compression” (compressão dupla)

Compressão dupla acontece quando o upstream (aplicação) já envia conteúdo comprimido e o Nginx tenta comprimir novamente, ou quando você serve arquivos já comprimidos (ex.: .gz) e ainda habilita gzip indiscriminadamente. Isso pode gerar:

  • Respostas inválidas (cliente não consegue descomprimir corretamente)
  • CPU desperdiçada
  • Headers incoerentes (ex.: Content-Encoding duplicado)

Uma configuração segura evita comprimir tipos que já são comprimidos e, em cenários de proxy, garante que o Nginx não tente recomprimir algo que já veio com Content-Encoding.

Habilitando gzip de forma segura (passo a passo)

1) Ative gzip e defina um conjunto seguro de tipos

O gzip no Nginx é controlado por diretivas no contexto http, server ou location. Em geral, faz sentido configurar no http para valer globalmente, ajustando exceções quando necessário.

gzip on; gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml; gzip_min_length 1024; gzip_comp_level 5; gzip_vary on; gzip_proxied any;

O que cada parte faz:

  • gzip on;: habilita compressão gzip.
  • gzip_types ...;: lista de MIME types que serão comprimidos. Foque em texto. Evite imagens/vídeos/arquivos compactados.
  • gzip_min_length 1024;: só comprime respostas acima de 1 KB (ajuste conforme seu cenário). Ajuda a evitar overhead em respostas pequenas.
  • gzip_comp_level 5;: nível de compressão (1–9). Valores médios (4–6) costumam equilibrar bem CPU vs redução. Níveis altos aumentam CPU para ganhos marginais.
  • gzip_vary on;: adiciona Vary: Accept-Encoding.
  • gzip_proxied any;: permite gzip mesmo quando há proxies no caminho (útil em reverse proxy). Em ambientes mais restritivos, você pode limitar (ex.: expired, no-cache, etc.).

2) Garanta que o tipo correto está sendo detectado

O Nginx decide o Content-Type com base em mapeamento de extensões para MIME types. Se o tipo estiver errado, o gzip pode não ser aplicado (ou pode ser aplicado indevidamente). Em configurações padrão, isso vem do arquivo de tipos MIME incluído. Se você notar que .js ou .json não estão sendo comprimidos, verifique se o Content-Type está correto na resposta.

3) Evite comprimir conteúdo já comprimido

Uma regra prática segura é: não inclua tipos binários comuns em gzip_types. Se você serve downloads (ex.: .zip), não os inclua. Se você tem endpoints que já retornam gzip (por exemplo, a aplicação já comprime), o Nginx normalmente não recomprime se a resposta já vier com Content-Encoding, mas é importante não forçar comportamentos estranhos com regras agressivas.

4) (Opcional) gzip_static para arquivos pré-comprimidos

Se você gera arquivos estáticos pré-comprimidos (por exemplo, app.js.gz ao lado de app.js), o Nginx pode servir diretamente a versão .gz quando o cliente aceita gzip. Isso economiza CPU porque não comprime em tempo real.

gzip_static on;

Observações:

  • Requer que você mantenha os arquivos .gz atualizados junto dos originais.
  • Não é obrigatório para ter gzip; é uma otimização.

5) Verifique se não está comprimindo duas vezes via pipeline

Em cenários com CDN ou proxy na frente, pode haver compressão em mais de um ponto. O ideal é escolher onde comprimir (CDN ou Nginx) e manter consistente. Se a CDN já comprime, você pode desabilitar gzip no Nginx para reduzir CPU, ou manter gzip no Nginx e configurar a CDN para respeitar o origin sem recomprimir (depende do provedor).

Brotli (br): visão conceitual e uso recomendado

O que é brotli e por que ele existe

Brotli é um algoritmo de compressão moderno (header Content-Encoding: br) que costuma oferecer melhor taxa de compressão que gzip para conteúdo textual, frequentemente com tamanhos menores. Em geral:

  • Melhor compressão: reduz mais bytes em HTML/CSS/JS/JSON.
  • Custo de CPU: pode ser maior para comprimir, especialmente em níveis altos.
  • Descompressão: geralmente eficiente em navegadores modernos.

Compatibilidade e negociação

Clientes informam suporte via Accept-Encoding. Navegadores modernos suportam br amplamente, mas alguns clientes antigos ou ferramentas podem não suportar. Por isso, o ideal é oferecer brotli quando o cliente pede e manter gzip como fallback.

Dependências e como pensar a adoção (sem instalação avançada)

Ao contrário do gzip (que vem nativamente no Nginx), brotli normalmente depende de um módulo adicional (varia por distribuição/pacote). Conceitualmente, você tem três cenários:

  • Nginx com módulo brotli disponível: você habilita brotli on; e define brotli_types semelhantes aos do gzip.
  • Sem módulo brotli: mantenha gzip bem configurado; já resolve a maior parte dos ganhos.
  • Arquivos pré-comprimidos: algumas pipelines geram .br e .gz para assets estáticos; o servidor escolhe conforme Accept-Encoding.

Uma estratégia segura e comum é: habilitar brotli para assets estáticos (CSS/JS) e manter gzip como fallback, evitando níveis extremos de brotli em compressão on-the-fly se a CPU for uma preocupação.

Exemplo conceitual de configuração (se o módulo existir)

Os nomes das diretivas podem variar conforme o módulo, mas a ideia é:

brotli on; brotli_comp_level 5; brotli_types text/plain text/css application/javascript application/json application/xml image/svg+xml;

Boas práticas:

  • Use níveis médios (por exemplo 4–6) para equilibrar CPU.
  • Não comprima binários já comprimidos.
  • Mantenha gzip habilitado para compatibilidade.

Laboratório: verificar Content-Encoding e comparar tamanho transferido com curl

Objetivo

  • Confirmar se o servidor está enviando Content-Encoding: gzip (ou br).
  • Comparar o tamanho transferido com e sem compressão.

Preparação

Escolha uma URL que retorne conteúdo textual e com tamanho razoável (por exemplo, um arquivo .css, .js ou uma resposta JSON). Nos exemplos abaixo, substitua https://seu-dominio.exemplo/arquivo.js pela sua URL.

1) Ver headers sem pedir compressão

Envie uma requisição indicando que você não aceita compressão (ou aceitando apenas identity):

curl -sI -H 'Accept-Encoding: identity' https://seu-dominio.exemplo/arquivo.js

Procure por:

  • Content-Encoding: idealmente ausente (ou não gzip/br)
  • Content-Length: tamanho do corpo sem compressão (quando presente)
  • Vary: Accept-Encoding: recomendado quando há compressão habilitada

2) Ver headers pedindo gzip

curl -sI -H 'Accept-Encoding: gzip' https://seu-dominio.exemplo/arquivo.js

Você deve ver:

  • Content-Encoding: gzip
  • Vary: Accept-Encoding
  • Content-Length menor (nem sempre aparece, dependendo de chunked/HTTP2)

3) (Opcional) Ver headers pedindo brotli

Se você tiver brotli habilitado e o cliente suportar, teste:

curl -sI -H 'Accept-Encoding: br' https://seu-dominio.exemplo/arquivo.js

Procure por Content-Encoding: br. Se não aparecer, pode ser que o módulo não esteja ativo, que o tipo não esteja em brotli_types ou que haja algum intermediário alterando a resposta.

4) Comparar tamanho transferido (bytes baixados) com e sem compressão

Para comparar o tamanho efetivamente transferido na rede, use curl com saída descartada e estatísticas de tamanho. O campo size_download representa bytes baixados do corpo (já comprimido, se houver compressão).

Sem compressão:

curl -s -o /dev/null -H 'Accept-Encoding: identity' -w 'download_bytes=%{size_download}\n' https://seu-dominio.exemplo/arquivo.js

Com gzip:

curl -s -o /dev/null -H 'Accept-Encoding: gzip' -w 'download_bytes=%{size_download}\n' https://seu-dominio.exemplo/arquivo.js

Se brotli estiver disponível:

curl -s -o /dev/null -H 'Accept-Encoding: br' -w 'download_bytes=%{size_download}\n' https://seu-dominio.exemplo/arquivo.js

Interpretação:

  • Se download_bytes com gzip/br for significativamente menor, a compressão está funcionando e trazendo ganho.
  • Se a diferença for pequena, o conteúdo pode ser pequeno, pouco compressível, ou já estar otimizado/minificado.

5) Validar que não há compressão dupla

Faça uma checagem simples: quando você pede gzip, deve haver no máximo um Content-Encoding (por exemplo, apenas gzip). Verifique:

curl -sI -H 'Accept-Encoding: gzip' https://seu-dominio.exemplo/arquivo.js | grep -i 'content-encoding'

Se você observar algo inesperado (por exemplo, múltiplos encodings ou comportamento inconsistente entre requisições), revise:

  • Se o upstream já está comprimindo respostas
  • Se há CDN/proxy recomprimindo
  • Se você está servindo arquivos pré-comprimidos e também aplicando compressão dinâmica de forma indevida

Agora responda o exercício sobre o conteúdo:

Em um ambiente com cache (CDN/proxy), qual prática ajuda a evitar que um cliente receba uma versão de resposta com compressão incorreta?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Como clientes podem pedir encodings diferentes via Accept-Encoding, o cache precisa variar a chave conforme esse header. O Vary: Accept-Encoding evita servir uma resposta gzip/br para quem não solicitou (ou o contrário).

Próximo capitúlo

Cache básico no Nginx: cache de proxy e controle de validade

Arrow Right Icon
Capa do Ebook gratuito Nginx para Iniciantes: Servidor Web, Reverse Proxy e Balanceamento Básico
77%

Nginx para Iniciantes: Servidor Web, Reverse Proxy e Balanceamento Básico

Novo curso

13 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.