Optimisation des tampons Nginx : réglage de `client_body_buffer_size` et `proxy_buffer_size`

Réglez les tampons de requêtes et de proxy Nginx sans gaspiller de mémoire ni provoquer une mise en mémoire tampon sur disque évitable.

Optimisation des tampons Nginx : réglage de client_body_buffer_size et proxy_buffer_size

L'optimisation des tampons Nginx ne consiste pas à rendre chaque tampon énorme. Il s'agit de décider quelles données doivent résider en mémoire, lesquelles peuvent être déversées en toute sécurité sur le disque et quelles réponses doivent être diffusées au lieu d'être entièrement mises en mémoire tampon.

La partie déroutante est que plusieurs directives se ressemblent. client_body_buffer_size s'applique aux corps de requête provenant des clients. proxy_buffer_size et proxy_buffers s'appliquent aux réponses provenant d'un serveur en amont lorsque Nginx agit comme un proxy inverse. FastCGI a ses propres directives correspondantes. Si vous réglez le mauvais côté, rien ne s'améliore.

Une bonne session de réglage commence par les symptômes :

  • Les journaux d'erreurs Nginx mentionnent des fichiers temporaires.
  • Les téléchargements ou les requêtes POST volumineuses semblent lents.
  • Les réponses API proxy inversé marquent une pause sous charge.
  • Les workers utilisent plus de mémoire que prévu.
  • Les en-têtes de réponse volumineux déclenchent des erreurs en amont.

Ces symptômes n'ont pas tous la même solution. Des tampons plus grands peuvent réduire les E/S disque, mais ils augmentent également la pression mémoire par requête. Sur un site très fréquenté, un paramètre qui semble minuscule par requête peut devenir important lorsqu'il est multiplié par des milliers de connexions simultanées.

Mise en mémoire tampon du corps de la requête : client_body_buffer_size

client_body_buffer_size contrôle le tampon utilisé pendant que Nginx lit le corps d'une requête client. Pensez aux soumissions de formulaires, aux corps JSON POST et aux téléchargements. Si le corps de la requête est plus grand que ce tampon, Nginx peut en écrire une partie dans un fichier temporaire sous client_body_temp_path.

Un paramètre de base ressemble à ceci :

http {
    client_body_buffer_size 128k;
}

Cela ne change pas la taille de téléchargement maximale autorisée. Celle-ci est contrôlée par client_max_body_size :

server {
    client_max_body_size 20m;
    client_body_buffer_size 128k;
}

Ces deux directives répondent à des questions différentes. client_max_body_size dit : « Quelle peut être la taille de la requête avant que Nginx ne la rejette ? » client_body_buffer_size dit : « Quelle quantité du corps de la requête Nginx doit-il conserver en mémoire avant d'utiliser un fichier temporaire ? »

Pour une API JSON où la plupart des corps de requête font moins de 32 Ko et quelques-uns 200 Ko, un tampon de 128k peut réduire les écritures de fichiers temporaires sans être excessif. Pour un service de téléchargement de fichiers qui accepte des vidéos de 50 Mo, définir client_body_buffer_size 50m globalement serait un mauvais compromis. Vous réserveriez trop de mémoire pour une charge de travail où la mise en mémoire tampon sur disque peut être acceptable.

Vérifiez le journal des erreurs pour des indices comme :

[warn] a client request body is buffered to a temporary file

Cet avertissement n'est pas automatiquement un échec. C'est Nginx qui vous dit qu'un corps a dépassé le tampon en mémoire. Si cela se produit occasionnellement lors de gros téléchargements, pas de problème. Si cela se produit constamment pour des requêtes API ordinaires, réglez le tampon ou corrigez les clients qui envoient des charges utiles volumineuses.

Mise en mémoire tampon des réponses proxy : proxy_buffer_size et proxy_buffers

Lorsque Nginx fait office de proxy vers une application en amont, la mise en mémoire tampon des réponses est généralement activée par défaut. Nginx lit rapidement la réponse en amont, la stocke dans des tampons et l'envoie au client. Si la réponse ne tient pas dans les tampons mémoire, une partie peut être écrite dans un fichier temporaire. La documentation officielle du module proxy Nginx décrit ce comportement et lie l'écriture de fichiers temporaires à proxy_max_temp_file_size et proxy_temp_file_write_size.

Le premier tampon est contrôlé par proxy_buffer_size :

proxy_buffer_size 16k;

Ce tampon gère l'en-tête de réponse et la première partie de la réponse. Si votre amont envoie des en-têtes volumineux, comme de nombreux cookies, des en-têtes d'authentification volumineux ou des métadonnées de traçage surdimensionnées, vous pouvez voir des erreurs comme « upstream sent too big header ». Dans ce cas, proxy_buffer_size est souvent la directive à examiner.

Les tampons de réponse restants sont contrôlés par proxy_buffers :

proxy_buffers 8 32k;

Cela signifie huit tampons de 32 Ko chacun pour les données de réponse proxyfiées. Cela ne signifie pas que chaque requête consomme toujours la totalité du début à la fin, mais vous devez toujours traiter ces paramètres comme des paramètres mémoire par requête sous charge.

Un bloc de proxy inverse courant ressemble à ceci :

location /api/ {
    proxy_pass http://app_backend;

    proxy_buffering on;
    proxy_buffer_size 16k;
    proxy_buffers 8 32k;
    proxy_busy_buffers_size 64k;
}

Ne copiez pas cela aveuglément. Un petit site HTML, une API JSON et un service de téléchargement de fichiers ont des besoins différents.

Fichiers temporaires et proxy_max_temp_file_size

Si la mise en mémoire tampon des réponses proxyfiées est activée et que la réponse est plus grande que les tampons configurés, Nginx peut en écrire une partie sur le disque. proxy_max_temp_file_size limite la taille de ce fichier temporaire. La valeur par défaut de Nginx est généralement documentée comme 1024m, et la définir sur 0 désactive la mise en mémoire tampon des réponses proxyfiées dans des fichiers temporaires.

location /api/ {
    proxy_pass http://app_backend;
    proxy_buffering on;
    proxy_max_temp_file_size 50m;
}

Utilisez 0 avec précaution :

proxy_max_temp_file_size 0;

Cela ne signifie pas que les réponses volumineuses deviennent gratuites. Cela signifie que Nginx n'utilisera pas de fichiers temporaires pour ces réponses proxyfiées mises en mémoire tampon. En fonction de la réponse, de la vitesse du client et du comportement de mise en mémoire tampon, vous pouvez avoir besoin de plus de mémoire, de paramètres de mise en mémoire tampon différents ou d'une conception de diffusion en continu.

Une nuance importante de la documentation Nginx : la restriction proxy_max_temp_file_size ne s'applique pas aux réponses qui seront mises en cache ou stockées sur le disque. Si vous utilisez proxy_cache ou proxy_store, examinez ces paramètres séparément.

Quand proxy_buffering off est la meilleure réponse

Parfois, vous ne devez pas du tout mettre la réponse en mémoire tampon. Les points de terminaison de diffusion en continu, les événements envoyés par le serveur, les téléchargements volumineux et les sorties de longue durée fonctionnent souvent mieux avec la mise en mémoire tampon du proxy désactivée :

location /events/ {
    proxy_pass http://app_backend;
    proxy_buffering off;
}

Avec la mise en mémoire tampon désactivée, Nginx transmet la réponse en amont au client au fur et à mesure qu'il la reçoit au lieu d'essayer de collecter la réponse complète. proxy_buffer_size compte toujours car Nginx a besoin d'un tampon pour les données reçues de l'amont, mais proxy_buffers et le comportement des fichiers temporaires deviennent moins centraux.

Ne désactivez pas la mise en mémoire tampon globalement à moins de comprendre le compromis. La mise en mémoire tampon protège les serveurs en amont des clients lents. Si les clients téléchargent lentement et que la mise en mémoire tampon est désactivée, les connexions en amont peuvent rester occupées plus longtemps.

Tampons FastCGI pour PHP et configurations similaires

Si Nginx communique avec PHP-FPM, les directives proxy_* ne sont pas celles dont vous avez besoin. FastCGI utilise des noms correspondants :

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;

    fastcgi_buffer_size 16k;
    fastcgi_buffers 8 32k;
}

La même logique s'applique : le premier tampon gère les en-têtes et les données initiales ; le tableau de tampons gère plus de contenu de réponse. Si PHP envoie des en-têtes volumineux, fastcgi_buffer_size peut nécessiter une attention particulière. Si les pages générées par PHP volumineuses se déversent sur le disque, examinez fastcgi_buffers et la charge de travail.

Comment régler sans deviner

Commencez par vérifier les journaux :

sudo tail -f /var/log/nginx/error.log

Recherchez les avertissements concernant les corps de requête ou les réponses en amont mises en mémoire tampon dans des fichiers temporaires. Ensuite, mesurez les tailles réelles impliquées. Pour les API, échantillonnez les tailles de requêtes et de réponses à partir des journaux d'accès, des journaux d'application ou de votre système d'observabilité. Pour les téléchargements, séparez les requêtes de métadonnées des points de terminaison de téléchargement de fichiers. Ils ne doivent pas partager les mêmes hypothèses.

Réglez dans le contexte le plus étroit qui a du sens. Au lieu de ceci :

http {
    client_body_buffer_size 10m;
}

préférez quelque chose comme ceci pour le point de terminaison de téléchargement uniquement :

location /upload/ {
    client_max_body_size 50m;
    client_body_buffer_size 512k;
    proxy_pass http://upload_backend;
}

Pour une API JSON avec des réponses occasionnelles de 1 Mo, vous pouvez régler uniquement cet emplacement 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;
}

Calculez ensuite le pire des cas. Si 1 000 requêtes simultanées peuvent chacune utiliser plusieurs centaines de Ko de tampons de réponse, cela représente une mémoire réelle. Laissez de la place pour les processus workers, TLS, les connexions en amont, les caches, le cache de pages du système d'exploitation et d'autres services.

Validez la configuration et surveillez le système

Après chaque modification :

sudo nginx -t
sudo systemctl reload nginx

Surveillez ensuite la mémoire, les E/S disque et les journaux d'erreurs. Un test de syntaxe réussi prouve seulement que le fichier s'analyse. Il ne prouve pas que les valeurs sont bonnes.

Les vérifications utiles incluent :

free -h
iostat -xz 1
sudo tail -f /var/log/nginx/error.log

Si les avertissements de fichiers temporaires disparaissent mais que la pression mémoire augmente, vous avez dépassé la cible. Si la mémoire reste saine et que les E/S disque diminuent pendant la charge de travail concernée, la modification a probablement aidé.

L'optimisation des tampons Nginx fonctionne mieux lorsqu'elle est locale, mesurée et liée au trafic réel. Gardez les requêtes ordinaires en mémoire lorsque cela permet de gagner en latence, laissez les téléchargements ou téléchargements vraiment volumineux utiliser le chemin approprié et évitez les paramètres globaux qui font payer chaque connexion pour le cas extrême le plus important.