Ajuste de Buffers do Nginx: Otimizando `client_body_buffer_size` e `proxy_buffer_size`
Ajuste os buffers de requisição e proxy do Nginx sem desperdiçar memória ou causar buffering em disco desnecessário.
Ajuste de Buffers do Nginx: Otimizando client_body_buffer_size e proxy_buffer_size
O ajuste de buffers do Nginx não se trata de tornar cada buffer enorme. Trata-se de decidir quais dados devem ficar na memória, quais podem ser gravados em disco com segurança e quais respostas devem ser transmitidas em fluxo contínuo em vez de serem totalmente armazenadas em buffer.
A parte confusa é que várias diretivas têm nomes semelhantes. client_body_buffer_size se aplica aos corpos das requisições vindas dos clientes. proxy_buffer_size e proxy_buffers se aplicam às respostas vindas de um servidor upstream quando o Nginx atua como proxy reverso. FastCGI tem suas próprias diretivas correspondentes. Se você ajustar o lado errado, nada melhora.
Uma boa sessão de ajuste começa com os sintomas:
- Os logs de erro do Nginx mencionam arquivos temporários.
- Uploads ou requisições POST grandes parecem lentos.
- Respostas de API com proxy reverso pausam sob carga.
- Os workers usam mais memória do que o esperado.
- Cabeçalhos de resposta grandes geram erros upstream.
Esses sintomas não têm todos a mesma correção. Buffers maiores podem reduzir I/O de disco, mas também aumentam a pressão de memória por requisição. Em um site movimentado, uma configuração que parece pequena por requisição pode se tornar grande quando multiplicada por milhares de conexões simultâneas.
Buffering do corpo da requisição: client_body_buffer_size
client_body_buffer_size controla o buffer usado enquanto o Nginx lê o corpo de uma requisição do cliente. Pense em envios de formulários, corpos JSON POST e uploads. Se o corpo da requisição for maior que este buffer, o Nginx pode gravar parte dele em um arquivo temporário em client_body_temp_path.
Uma configuração básica se parece com isso:
http {
client_body_buffer_size 128k;
}
Isso não altera o tamanho máximo permitido para upload. Isso é controlado por client_max_body_size:
server {
client_max_body_size 20m;
client_body_buffer_size 128k;
}
Essas duas diretivas respondem a perguntas diferentes. client_max_body_size diz: "Qual o tamanho máximo que a requisição pode ter antes do Nginx rejeitá-la?" client_body_buffer_size diz: "Quanto do corpo da requisição o Nginx deve manter na memória antes de usar um arquivo temporário?"
Para uma API JSON onde a maioria dos corpos de requisição tem menos de 32 KB e alguns têm 200 KB, um buffer de 128k pode reduzir as gravações em arquivos temporários sem ser excessivo. Para um serviço de upload de arquivos que aceita vídeos de 50 MB, definir client_body_buffer_size 50m globalmente seria uma má troca. Você estaria reservando muita memória para uma carga de trabalho onde o buffering em disco pode ser aceitável.
Verifique o log de erro em busca de pistas como:
[warn] a client request body is buffered to a temporary file
Esse aviso não é automaticamente uma falha. É o Nginx informando que um corpo excedeu o buffer em memória. Se isso acontecer ocasionalmente durante uploads grandes, tudo bem. Se acontecer constantemente para requisições normais de API, ajuste o buffer ou corrija os clientes que enviam payloads grandes.
Buffering de resposta de proxy: proxy_buffer_size e proxy_buffers
Quando o Nginx faz proxy para um aplicativo upstream, o buffering de resposta geralmente está ativado por padrão. O Nginx lê a resposta upstream rapidamente, armazena em buffers e a envia para o cliente. Se a resposta não couber nos buffers de memória, parte dela pode ser gravada em um arquivo temporário. A documentação oficial do módulo de proxy do Nginx descreve esse comportamento e vincula a gravação em arquivo temporário a proxy_max_temp_file_size e proxy_temp_file_write_size.
O primeiro buffer é controlado por proxy_buffer_size:
proxy_buffer_size 16k;
Este buffer lida com o cabeçalho da resposta e a primeira parte da resposta. Se o seu upstream enviar cabeçalhos grandes, como muitos cookies, cabeçalhos de autenticação grandes ou metadados de rastreamento excessivos, você pode ver erros como "upstream sent too big header". Nesse caso, proxy_buffer_size é geralmente a diretiva a ser revisada.
Os buffers de resposta restantes são controlados por proxy_buffers:
proxy_buffers 8 32k;
Isso significa oito buffers de 32 KB cada para dados de resposta com proxy. Isso não significa que toda requisição consome o valor total do início ao fim, mas você ainda deve tratar essas configurações como configurações de memória por requisição sob carga.
Um bloco de proxy reverso comum se parece com isso:
location /api/ {
proxy_pass http://app_backend;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
}
Não copie isso cegamente. Um site HTML pequeno, uma API JSON e um serviço de download de arquivos têm necessidades diferentes.
Arquivos temporários e proxy_max_temp_file_size
Se o buffering de resposta com proxy estiver ativado e a resposta for maior que os buffers configurados, o Nginx pode gravar parte dela em disco. proxy_max_temp_file_size limita o tamanho desse arquivo temporário. O padrão do Nginx é comumente documentado como 1024m, e defini-lo como 0 desabilita o buffering de respostas com proxy em arquivos temporários.
location /api/ {
proxy_pass http://app_backend;
proxy_buffering on;
proxy_max_temp_file_size 50m;
}
Use 0 com cuidado:
proxy_max_temp_file_size 0;
Isso não significa que respostas grandes se tornam gratuitas. Significa que o Nginx não usará arquivos temporários para essas respostas com proxy armazenadas em buffer. Dependendo da resposta, da velocidade do cliente e do comportamento do buffering, você pode precisar de mais memória, configurações de buffer diferentes ou um design de streaming.
Uma nuance importante da documentação do Nginx: a restrição proxy_max_temp_file_size não se aplica a respostas que serão armazenadas em cache ou em disco. Se você usar proxy_cache ou proxy_store, revise essas configurações separadamente.
Quando proxy_buffering off é a melhor resposta
Às vezes você não deve armazenar a resposta em buffer. Endpoints de streaming, eventos enviados pelo servidor, downloads grandes e saídas de longa duração geralmente funcionam melhor com o buffering de proxy desativado:
location /events/ {
proxy_pass http://app_backend;
proxy_buffering off;
}
Com o buffering desativado, o Nginx passa a resposta upstream para o cliente à medida que a recebe, em vez de tentar coletar a resposta completa. O proxy_buffer_size ainda importa porque o Nginx precisa de um buffer para os dados recebidos do upstream, mas proxy_buffers e o comportamento do arquivo temporário se tornam menos centrais.
Não desative o buffering globalmente a menos que você entenda a troca. O buffering protege os servidores upstream de clientes lentos. Se os clientes baixarem lentamente e o buffering estiver desativado, as conexões upstream podem permanecer ocupadas por mais tempo.
Buffers FastCGI para PHP e configurações semelhantes
Se o Nginx se comunica com PHP-FPM, as diretivas proxy_* não são as que você precisa. FastCGI usa nomes correspondentes:
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_buffer_size 16k;
fastcgi_buffers 8 32k;
}
A mesma lógica se aplica: o primeiro buffer lida com cabeçalhos e dados iniciais; o array de buffers lida com mais conteúdo da resposta. Se o PHP enviar cabeçalhos grandes, fastcgi_buffer_size pode precisar de atenção. Se páginas grandes geradas por PHP forem gravadas em disco, revise fastcgi_buffers e a carga de trabalho.
Como ajustar sem adivinhar
Comece verificando os logs:
sudo tail -f /var/log/nginx/error.log
Procure avisos sobre corpos de requisição ou respostas upstream sendo armazenados em buffer em arquivos temporários. Em seguida, meça os tamanhos reais envolvidos. Para APIs, amostre os tamanhos de requisição e resposta dos logs de acesso, logs de aplicação ou seu sistema de observabilidade. Para uploads, separe as requisições de metadados dos endpoints de upload de arquivos. Eles não devem compartilhar as mesmas suposições.
Ajuste no contexto mais restrito que fizer sentido. Em vez disso:
http {
client_body_buffer_size 10m;
}
prefira algo como isso apenas para o endpoint de upload:
location /upload/ {
client_max_body_size 50m;
client_body_buffer_size 512k;
proxy_pass http://upload_backend;
}
Para uma API JSON com respostas ocasionais de 1 MB, você pode ajustar apenas essa localização da API:
location /reports/ {
proxy_pass http://report_backend;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 16 64k;
proxy_max_temp_file_size 20m;
}
Em seguida, calcule o pior caso. Se 1.000 requisições simultâneas podem usar cada uma várias centenas de KB de buffers de resposta, isso é memória real. Deixe espaço para processos workers, TLS, conexões upstream, caches, o cache de página do SO e outros serviços.
Valide a configuração e monitore o sistema
Após cada alteração:
sudo nginx -t
sudo systemctl reload nginx
Em seguida, monitore memória, I/O de disco e logs de erro. Um teste de sintaxe bem-sucedido apenas prova que o arquivo é analisado. Não prova que os valores são bons.
Verificações úteis incluem:
free -h
iostat -xz 1
sudo tail -f /var/log/nginx/error.log
Se os avisos de arquivo temporário desaparecerem, mas a pressão de memória aumentar, você exagerou. Se a memória permanecer saudável e o I/O de disco cair durante a carga de trabalho afetada, a mudança provavelmente ajudou.
O ajuste de buffers do Nginx funciona melhor quando é local, medido e vinculado ao tráfego real. Mantenha requisições comuns na memória quando isso economizar latência, deixe uploads ou downloads genuinamente grandes usarem o caminho correto e evite configurações globais que fazem cada conexão pagar pelo maior caso extremo.