Otimização de desempenho do Nginx: gzip, cache e pools de conexões

Na semana passada, recebi um alerta: o tempo de carregamento da página inicial de um e-commerce tinha disparado para 4 segundos. Abri o Chrome DevTools e encontrei um HTML de 120 KB, mais 350 KB de CSS e JavaScript — todos arquivos originais, sem compactação. Para piorar, cada requisição chegava ao backend, e a taxa de acerto do cache era de apenas 12%. Naquela noite, passei duas horas ajustando o Nginx: ativei a compactação gzip, completei a estratégia de cache e corrigi os parâmetros dos pools de conexões. Na manhã seguinte, a página inicial já carregava em 1,6 segundo, e o QPS do backend havia caído quase pela metade.
Esse problema é muito comum. Muita gente instala o Nginx e o deixa funcionando com gzip desativado por padrão, duas linhas improvisadas de cache e o limite padrão de conexões. Quando chega um pico de tráfego, o servidor não aguenta.
Reuni aqui as configurações de otimização de desempenho do Nginx que validei em produção. A compactação gzip pode reduzir o volume transferido em 60% a 80%; com 95% de acerto do cache, a carga do backend pode cair 90%; e uma configuração adequada dos pools de conexões pode multiplicar a capacidade simultânea por três ou quatro. Vou detalhar cada módulo, os problemas que encontrei e os números medidos nos testes.
Capítulo 1: configuração do gzip — reduza o volume transferido
Primeiro, vale entender por que o gzip é tão importante. Imagine um arquivo HTML de 100 KB. Depois da compactação gzip, ele pode ocupar apenas 20 a 25 KB. Esses 75 a 80 KB economizados representam carregamento mais rápido para o usuário e menor custo de tráfego para você.
Percebi isso pela primeira vez em um projeto de cliente. Mais de 60% dos usuários acessavam pelo celular, muitos deles em redes 4G. A página inicial levava de 3 a 4 segundos para carregar, e a taxa de rejeição havia subido para 70%. Depois de ativar o gzip, o volume transferido caiu 70%, e o carregamento da primeira tela chegou a cerca de 1,5 segundo.
1.1 Configuração básica: primeiro, faça funcionar
A configuração de gzip no Nginx não é complicada. O essencial cabe em poucas linhas:
http {
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}
gzip on é o interruptor e dispensa explicação. Já gzip_vary on é essencial: ele adiciona Vary: Accept-Encoding ao cabeçalho de resposta para informar ao CDN e ao navegador que o conteúdo varia conforme a capacidade de compactação do cliente, evitando inconsistências no cache.
Definir gzip_min_length como 1000 bytes significa não compactar arquivos menores que 1 KB. Arquivos muito pequenos trazem pouco ganho com a compactação e ainda consomem CPU. gzip_types especifica os tipos MIME que serão compactados. Por padrão, apenas text/html é incluído; você precisa adicionar CSS, JavaScript, JSON e XML.
1.2 Configuração avançada: nível de compactação e lista de tipos MIME
O nível de compactação exige equilíbrio. gzip_comp_level aceita valores de 1 a 9: quanto maior o número, maior a taxa de compactação, mas também maior o consumo de CPU.
Fiz uma série de testes:
| Nível de compactação | Taxa de compactação do HTML | Tempo de CPU (ms) | Cenário recomendado |
|---|---|---|---|
| 1 | 65% | 2 | CPU limitada |
| 4 | 72% | 3 | Equilíbrio (recomendado) |
| 6 | 75% | 5 | Largura de banda limitada (recomendado) |
| 9 | 78% | 12 | Cenários extremos |
Os níveis 4 e 6 são as melhores escolhas na maioria dos casos. O nível 9 dobra o consumo de CPU e melhora a taxa de compactação em apenas alguns pontos percentuais, por isso não compensa.
Esta é a configuração completa de gzip que uso em produção:
# Configuração de compactação gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/xml+rss
application/xhtml+xml
application/x-javascript;
gzip_disable "msie6";
O parâmetro gzip_proxied any costuma passar despercebido. Se o Nginx atua como proxy reverso e o cabeçalho retornado pelo backend não contém Content-Length, a resposta não é compactada por padrão. Definir o valor como any força a compactação de todas as respostas elegíveis.
gzip_disable "msie6" existe para compatibilidade com versões antigas do IE6, que têm problemas com gzip. Hoje o IE6 praticamente desapareceu e essa linha pode ser removida, mas costumo mantê-la por precaução.
1.3 Quais tipos de arquivo oferecem o maior ganho?
Nos meus testes, o resultado da compactação varia bastante conforme o tipo de arquivo:
| Tipo de arquivo | Tamanho original | Após a compactação | Taxa de compactação |
|---|---|---|---|
| HTML | 100 KB | 20-25 KB | 75-80% |
| CSS | 80 KB | 24-28 KB | 65-70% |
| JavaScript | 120 KB | 36-42 KB | 65-70% |
| API JSON | 50 KB | 20-25 KB | 50-60% |
| Imagens/vídeos | Já compactados | Sem benefício | 0-5% |
Imagens e vídeos já são compactados em formatos como JPEG, PNG e MP4. Aplicar gzip novamente pode até aumentar o tamanho. Portanto, nunca inclua image/* nem video/* em gzip_types: isso produz o efeito contrário.
Uma vez, ao investigar um problema, encontrei image/jpeg na lista gzip_types. O tamanho das imagens tinha aumentado de 3% a 5%. É um erro básico que provavelmente também cometi quando ainda não entendia o assunto e queria incluir todos os tipos MIME possíveis.
Capítulo 2: estratégia de cache — acelere conteúdo estático
O cache oferece um dos ganhos mais diretos na otimização de desempenho. Com uma configuração adequada, 95% das requisições podem ser respondidas diretamente pelo Nginx, sem chegar ao backend. Já vi muitos sistemas com servidores de aplicação trabalhando no limite enquanto o cache do Nginx quase não era usado — não porque estivesse desativado, mas porque havia sido configurado incorretamente.
2.1 Como escolher entre proxy_cache e fastcgi_cache?
O Nginx oferece dois mecanismos de cache:
- proxy_cache: armazena em cache as respostas de servidores upstream e é indicado para proxy reverso, como serviços em Node.js, Python ou Go
- fastcgi_cache: armazena em cache as respostas de processos FastCGI e é indicado para PHP-FPM
A escolha depende da tecnologia do backend. Se você usa PHP, escolha fastcgi_cache; se usa Node.js, Python ou Go, escolha proxy_cache. A lógica de configuração é praticamente igual nos dois casos. Nos exemplos a seguir, usarei proxy_cache.
2.2 Configuração completa do proxy_cache
Primeiro, defina o caminho do cache no bloco http:
http {
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=my_cache:10m
max_size=10g
inactive=60m
use_temp_path=off;
}
Veja o que cada linha faz:
levels=1:2: define os níveis do diretório de cache; 1:2 cria uma estrutura de dois níveis para evitar arquivos demais em um único diretóriokeys_zone=my_cache:10m: define o nome da zona de cache e o espaço em memória para metadados; 10m comporta cerca de 80 mil chaves de cachemax_size=10g: define o tamanho total máximo; ao ultrapassá-lo, o Nginx remove itens conforme o algoritmo LRUinactive=60m: remove dados do cache que não forem acessados durante 60 minutosuse_temp_path=off: grava diretamente no diretório de cache e evita o custo de mover arquivos temporários
Em seguida, ative o cache em server ou location:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_cache my_cache;
# Validade do cache
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid any 1m;
# Desenho da chave do cache
proxy_cache_key $scheme$request_method$host$request_uri;
# Estratégia de fallback (detalhada a seguir)
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
# Cabeçalho com o status do cache (para depuração)
add_header X-Cache-Status $upstream_cache_status;
}
}
proxy_cache_valid é a configuração central e define a duração do cache para diferentes códigos de status:
200 302 10m: mantém respostas normais em cache por 10 minutos404 1m: mantém erros 404 em cache por 1 minuto e evita que requisições maliciosas sobrecarreguem o backendany 1m: mantém outros códigos de status em cache por 1 minuto
2.3 Desenho da chave e estratégia de invalidação do cache
A chave definida em proxy_cache_key determina quais requisições serão consideradas “iguais”. O padrão é $scheme$proxy_host$request_uri, mas recomendo declará-la explicitamente:
proxy_cache_key $scheme$request_method$host$request_uri;
Assim, a chave inclui protocolo, método da requisição, nome do host e URI completa. Se o site aceita GET e POST ou responde por vários domínios, essa configuração é mais precisa.
A invalidação do cache costuma dar trabalho. Estas são as estratégias mais comuns:
- Expiração por tempo: defina o período em
proxy_cache_valid; o cache expira automaticamente ao final - Desvio ativo: use
proxy_cache_bypasspara ignorar o cache - Limpeza de cache: a versão comercial Nginx Plus oferece
proxy_cache_purge
Normalmente uso a segunda opção e faço o controle por um cabeçalho de requisição:
# Ignora o cache quando um cabeçalho específico está presente
proxy_cache_bypass $http_x_nocache;
# Ou ignora por meio de um parâmetro específico
proxy_cache_bypass $arg_nocache;
Quando for necessário atualizar o cache, basta adicionar ?nocache=1 ou o cabeçalho X-Nocache: 1.
2.4 Estratégia de fallback: continue atendendo mesmo se o backend falhar
proxy_cache_use_stale é uma configuração muito útil. Se o backend apresentar erro ou timeout, o Nginx pode devolver conteúdo expirado do cache em vez de retornar um erro imediatamente.
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
No Dia dos Solteiros do ano passado, tivemos um problema ao ampliar a capacidade do backend, e o serviço de API passou a responder 502 de forma intermitente. Felizmente, o cache tinha essa estratégia de fallback. O acesso dos usuários praticamente não foi afetado: embora o conteúdo em cache tivesse expirado havia alguns minutos, ele ainda pôde ser entregue normalmente. Quando o backend se recuperou, o cache foi atualizado de forma automática.
Comparação dos dados medidos:
| Métrica | Sem cache | Acerto do cache | Modo de fallback |
|---|---|---|---|
| Tempo de resposta | 150-200 ms | 5-10 ms | 5-10 ms |
| QPS do backend | 1000 | 50 | 0 |
| Experiência do usuário | Normal | Normal | Um pouco mais lenta |
Quando há acerto do cache, o tempo de resposta cai de 200 ms para 5 a 10 ms, uma melhoria de quase 20 vezes. É um ganho muito fácil de perceber.
Capítulo 3: pools de conexões — essenciais para alta simultaneidade
gzip e cache resolvem “como transferir mais rápido”. Os pools de conexões resolvem “como atender mais requisições”. Com a configuração padrão, cada worker do Nginx aceita no máximo 1024 conexões simultâneas. Assim que o tráfego aumenta, esse limite deixa de ser suficiente.
3.1 worker_connections: calcule corretamente o limite
A fórmula para o número máximo de conexões simultâneas é:
Máximo de conexões simultâneas = worker_processes × worker_connections
Suponha que o servidor tenha uma CPU de 8 núcleos, worker_processes esteja definido como 8 — ou como auto, para ajuste automático — e worker_connections seja 4096:
Máximo de conexões simultâneas = 8 × 4096 = 32768
O número parece alto, mas lembre-se de que cada requisição costuma usar duas conexões: uma do cliente até o Nginx e outra do Nginx até o backend. Portanto, a quantidade real de requisições simultâneas que o servidor consegue processar fica aproximadamente pela metade.
A configuração fica no bloco events:
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
use epoll é o padrão no Linux e não precisa ser declarado, mas escrevê-lo deixa a intenção mais clara. multi_accept on permite que o worker aceite várias conexões novas de uma vez e reduz a fila em situações de alta simultaneidade.
3.2 keepalive do cliente: reutilize conexões e reduza o custo
Estabelecer uma conexão TCP exige um handshake de três vias, que tem seu custo. O keepalive permite reutilizar a conexão entre o cliente e o Nginx, sem precisar abrir outra a cada requisição.
http {
keepalive_timeout 65;
keepalive_requests 1000;
}
keepalive_timeout 65: mantém a conexão aberta por 65 segundos e depois a fecha. O valor não deve ser alto demais, pois consumiria recursos excessivos no servidor, nem baixo demais, pois reduziria a reutilização. Um intervalo de 60 a 75 segundos é razoável.
keepalive_requests 1000: permite processar até 1000 requisições em uma conexão. Um valor baixo demais causa desconexões frequentes; um valor alto demais pode provocar vazamento de recursos. Nos meus testes, 1000 foi um valor estável.
3.3 keepalive do upstream: pool de conexões com o backend
Muita gente não conhece essa configuração, mas o efeito é evidente. O Nginx também pode reutilizar conexões com os serviços de backend, reduzindo o custo de estabelecer conexões TCP repetidamente.
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 64;
keepalive_timeout 60s;
keepalive_requests 1000;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
keepalive 64: mantém um pool de 64 conexões ociosas. Ajuste o valor conforme o número de serviços de backend; em geral, use de quatro a oito vezes a quantidade de servidores.
As linhas proxy_http_version 1.1 e proxy_set_header Connection "" são obrigatórias. O HTTP/1.1 oferece keepalive por padrão, e é preciso limpar o cabeçalho Connection para reutilizar a conexão. Sem essas duas linhas, o keepalive do upstream não funciona.
Comparação dos resultados medidos:
| Configuração | Aberturas de conexão por minuto | Consumo de CPU | Cenário recomendado |
|---|---|---|---|
| Sem keepalive do upstream | 6000 | Alto | Baixo tráfego |
| keepalive 32 | 3000 | Médio | Tráfego médio |
| keepalive 64 | 1500 | Baixo | Alto tráfego |
Depois de ativar o keepalive do upstream, o custo de abertura das conexões caiu 50%. Essa configuração é especialmente importante em cenários de alta simultaneidade.
3.4 Tabela de referência dos parâmetros recomendados
Organizei uma configuração recomendada para diferentes cenários:
| Parâmetro | Baixo tráfego (<1000 QPS) | Tráfego médio (1000-5000 QPS) | Alto tráfego (>5000 QPS) |
|---|---|---|---|
| worker_processes | auto | auto | auto |
| worker_connections | 1024 | 2048 | 4096 |
| keepalive_timeout | 60 | 65 | 75 |
| keepalive_requests | 100 | 500 | 1000 |
| upstream keepalive | 16 | 32 | 64 |
Esses números são apenas um ponto de partida. O ajuste real precisa considerar os dados dos testes de carga. Normalmente uso wrk ou ab para medir as curvas de conexões e tempo de resposta e encontrar os melhores valores.
Capítulo 4: modelo completo de configuração — pronto para produção
Os três capítulos anteriores explicaram os princípios. Agora segue um modelo de configuração integrado. Você pode ajustar os parâmetros ao cenário real, mas a estrutura é reutilizável.
# Modelo de nginx.conf para produção
user nginx;
worker_processes auto;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
# Configuração de compactação gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml
application/xml+rss application/xhtml+xml;
gzip_disable "msie6";
# Configuração do caminho do cache
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=my_cache:10m
max_size=10g
inactive=60m
use_temp_path=off;
# Configuração das conexões dos clientes
keepalive_timeout 65;
keepalive_requests 1000;
# Grupo de servidores de backend
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 64;
keepalive_timeout 60s;
keepalive_requests 1000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
# Configuração de cache
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_key $scheme$request_method$host$request_uri;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
# Cabeçalho de resposta para depuração
add_header X-Cache-Status $upstream_cache_status;
}
}
}
Configurações específicas para três cenários
E-commerce: a página inicial e as páginas de produto mudam com frequência, então use um cache mais curto; de 10 a 15 minutos é suficiente. O keepalive do upstream deve ser maior, pois há muitas consultas ao banco de dados e a carga do backend é alta.
Serviços de API: os dados precisam ser mais atuais, então proxy_cache_valid pode ficar entre 1 e 5 minutos. gzip funciona muito bem com JSON e não deve ser esquecido.
Sites estáticos: HTML, CSS e JavaScript quase não mudam, então o cache pode durar 1 hora ou mais. Como os arquivos estáticos contêm bastante texto, o ganho com gzip é maior.
Capítulo 5: problemas comuns e solução de falhas
Aqui estão soluções diretas para alguns problemas frequentes que já encontrei.
P: a compactação gzip não funciona, e a resposta não contém Content-Encoding: gzip
Confira três pontos:
- se
gzip onestá no nível correto da configuração, no bloco http - se
gzip_typesinclui o tipo MIME da resposta - se o corpo da resposta é maior que
gzip_min_length
Teste com curl: curl -H "Accept-Encoding: gzip" -I http://your-site.com
P: a taxa de acerto do cache está baixa, e X-Cache-Status quase sempre mostra MISS
Causas comuns:
- a chave do cache foi mal projetada, e cada requisição é considerada “diferente”
proxy_cache_validé curto demais, e o cache expira antes de ser aproveitado- o backend devolve
Cache-Control: no-cacheouSet-Cookienos cabeçalhos de resposta
Verifique os cabeçalhos e confirme que não há instruções que impeçam o cache.
P: worker_connections é insuficiente, e aparecem erros 502
Consulte o log de erros do Nginx. Se houver a mensagem worker_connections are not enough, o número de conexões simultâneas ultrapassou o limite.
Soluções:
- aumente o valor de
worker_connections - verifique se há vazamento de conexões causado por uma configuração inadequada de keepalive
- considere adicionar servidores e distribuir a carga
P: o uso de memória está alto, e o servidor apresenta OOM com frequência
Possíveis causas:
- a
keys_zonedeproxy_cache_pathestá grande demais - há arquivos demais no cache, elevando o uso de memória mapeada
- o pool de conexões
keepaliveé grande demais, e conexões ociosas consomem recursos
Reduza esses parâmetros conforme necessário ou aumente a memória do servidor.
Para concluir
Os três pilares da otimização de desempenho do Nginx são compactação gzip, estratégia de cache e configuração dos pools de conexões. Quando funcionam juntos, eles podem dobrar a velocidade de carregamento do site e multiplicar por três ou quatro a capacidade simultânea.
A otimização não termina depois de uma única configuração. Recomendo seguir esta ordem:
- Ative primeiro o gzip: é a menor mudança e oferece o ganho mais direto; leva cerca de dez minutos
- Configure o cache em seguida: desenhe a estratégia conforme o cenário do negócio; dá para concluir em um dia
- Ajuste por último os pools de conexões: exige validação com testes de carga e faz mais sentido quando o tráfego já está estável
Depois de cada mudança, faça testes de carga e confirme o resultado. wrk e ab servem para acompanhar as variações do tempo de resposta, QPS e taxa de erros. Não ajuste por intuição: deixe os dados orientarem a decisão.
Use esta lista final de verificação:
- gzip está ativo, com todos os tipos MIME necessários
- gzip_comp_level está entre 4 e 6 para equilibrar CPU e taxa de compactação
- proxy_cache_path está configurado, com tamanho de cache adequado
- proxy_cache_valid está ajustado ao cenário do negócio
- a estratégia de fallback proxy_cache_use_stale está configurada
- worker_connections está definido como 4096 ou mais
- keepalive_timeout está entre 60 e 75 segundos
- o keepalive do upstream está configurado, incluindo HTTP/1.1 e o cabeçalho Connection
- o cabeçalho de resposta inclui X-Cache-Status para depuração
É isso. Se tiver alguma dúvida, deixe um comentário; tentarei responder.
Processo de configuração para otimizar o desempenho do Nginx
Configuração de produção em três etapas: compactação gzip, estratégia de cache e otimização dos pools de conexões
⏱️ Estimated time: 30 min
- 1
Step 1: Ativar a compactação gzip
Adicione a configuração ao bloco http:
• gzip on; ativa a compactação
• gzip_vary on; adiciona o cabeçalho de resposta Vary
• gzip_comp_level 6; define o nível de compactação (recomendado: 4 a 6)
• gzip_min_length 1000; não compacta arquivos menores que 1 KB
• gzip_types especifica os tipos MIME: text/plain text/css application/json application/javascript - 2
Step 2: Configurar o cache proxy_cache
Faça a configuração em duas etapas:
Etapa 1: defina o caminho do cache
• proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m
Etapa 2: ative o cache em location
• proxy_cache my_cache;
• proxy_cache_valid 200 10m; mantém respostas normais em cache por 10 minutos
• proxy_cache_use_stale error timeout http_502; configura a estratégia de fallback - 3
Step 3: Otimizar os parâmetros dos pools de conexões
Três configurações essenciais:
• worker_connections 4096; definida no bloco events
• keepalive_timeout 65; keepalive_requests 1000; definidas no bloco http
• upstream keepalive 64; proxy_http_version 1.1; proxy_set_header Connection ""; configuração do pool de conexões com o backend
FAQ
Qual nível de compactação gzip devo escolher?
Como escolher entre proxy_cache e fastcgi_cache?
Qual valor usar em worker_connections?
Como investigar uma taxa de acerto de cache baixa?
Por que o keepalive do upstream exige HTTP/1.1?
Como definir a duração do cache para diferentes cenários de negócio?
15 min de leitura · Publicado em: 11 abr 2026 · Atualizado em: 4 set 2026
Guia prático Nginx
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Guia completo de proxy reverso com Nginx: upstream, buffers e timeouts
Uma análise aprofundada das três configurações centrais de proxy reverso no Nginx: balanceamento de carga com upstream, ajuste de proxy buffers e definição de timeouts, incluindo diagnóstico de erros 502/504, pool de conexões keepalive, verificação de integridade e outras práticas úteis
Parte 1 de 2
Próximo
Este é o post mais recente da série até agora.



Comentários
Entre com GitHub para comentar