Nginx-Pufferoptimierung: Optimierung von `client_body_buffer_size` und `proxy_buffer_size`

Optimieren Sie Nginx-Anfrage- und Proxy-Puffer, ohne Speicher zu verschwenden oder vermeidbare Festplattenpufferung zu verursachen.

Nginx-Pufferoptimierung: Optimierung von client_body_buffer_size und proxy_buffer_size

Bei der Nginx-Pufferoptimierung geht es nicht darum, jeden Puffer riesig zu machen. Es geht darum zu entscheiden, welche Daten im Speicher bleiben sollen, welche Daten sicher auf die Festplatte ausgelagert werden können und welche Antworten gestreamt werden sollten, anstatt vollständig gepuffert zu werden.

Der verwirrende Teil ist, dass mehrere Direktiven ähnlich klingen. client_body_buffer_size gilt für Anforderungstexte, die von Clients kommen. proxy_buffer_size und proxy_buffers gelten für Antworten, die von einem Upstream-Server zurückkommen, wenn Nginx als Reverse-Proxy fungiert. FastCGI hat seine eigenen passenden Direktiven. Wenn Sie die falsche Seite optimieren, verbessert sich nichts.

Eine gute Optimierungssitzung beginnt mit Symptomen:

  • Nginx-Fehlerprotokolle erwähnen temporäre Dateien.
  • Uploads oder große POST-Anfragen fühlen sich langsam an.
  • Reverse-Proxy-API-Antworten pausieren unter Last.
  • Worker verbrauchen mehr Speicher als erwartet.
  • Große Antwort-Header verursachen Upstream-Fehler.

Diese Symptome haben nicht alle die gleiche Lösung. Größere Puffer können die Festplatten-E/A reduzieren, erhöhen aber auch den Speicherdruck pro Anfrage. Auf einer stark frequentierten Website kann eine Einstellung, die pro Anfrage klein aussieht, groß werden, wenn sie mit Tausenden von gleichzeitigen Verbindungen multipliziert wird.

Pufferung von Anforderungstexten: client_body_buffer_size

client_body_buffer_size steuert den Puffer, der verwendet wird, während Nginx den Text einer Client-Anforderung liest. Denken Sie an Formularübermittlungen, JSON-POST-Texte und Uploads. Wenn der Anforderungstext größer als dieser Puffer ist, kann Nginx einen Teil davon in eine temporäre Datei unter client_body_temp_path schreiben.

Eine grundlegende Einstellung sieht so aus:

http {
    client_body_buffer_size 128k;
}

Dies ändert nicht die maximal zulässige Upload-Größe. Diese wird durch client_max_body_size gesteuert:

server {
    client_max_body_size 20m;
    client_body_buffer_size 128k;
}

Diese beiden Direktiven beantworten unterschiedliche Fragen. client_max_body_size sagt: "Wie groß darf die Anforderung sein, bevor Nginx sie ablehnt?" client_body_buffer_size sagt: "Wie viel des Anforderungstextes soll Nginx im Speicher behalten, bevor es eine temporäre Datei verwendet?"

Für eine JSON-API, bei der die meisten Anforderungstexte unter 32 KB liegen und einige 200 KB groß sind, kann ein 128k-Puffer die Anzahl der temporären Dateischreibvorgänge reduzieren, ohne übermäßig zu sein. Für einen Datei-Upload-Dienst, der 50-MB-Videos akzeptiert, wäre die globale Einstellung client_body_buffer_size 50m ein schlechter Kompromiss. Sie würden zu viel Speicher für eine Arbeitslast reservieren, bei der die Festplattenpufferung akzeptabel sein kann.

Überprüfen Sie das Fehlerprotokoll auf Hinweise wie:

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

Diese Warnung ist nicht automatisch ein Fehler. Sie teilt Ihnen mit, dass ein Text den In-Memory-Puffer überschritten hat. Wenn dies gelegentlich bei großen Uploads passiert, ist das in Ordnung. Wenn es ständig bei gewöhnlichen API-Anfragen passiert, optimieren Sie den Puffer oder beheben Sie die Clients, die große Nutzlasten senden.

Proxy-Antwortpufferung: proxy_buffer_size und proxy_buffers

Wenn Nginx an eine Upstream-App weiterleitet, ist die Antwortpufferung normalerweise standardmäßig aktiviert. Nginx liest die Upstream-Antwort schnell, speichert sie in Puffern und sendet sie an den Client. Wenn die Antwort nicht in die Speicherpuffer passt, kann ein Teil davon in eine temporäre Datei geschrieben werden. Die offizielle Nginx-Proxy-Modul-Dokumentation beschreibt dieses Verhalten und verknüpft das Schreiben temporärer Dateien mit proxy_max_temp_file_size und proxy_temp_file_write_size.

Der erste Puffer wird durch proxy_buffer_size gesteuert:

proxy_buffer_size 16k;

Dieser Puffer verarbeitet den Antwort-Header und den ersten Teil der Antwort. Wenn Ihr Upstream große Header sendet, wie z. B. viele Cookies, große Authentifizierungs-Header oder übergroße Tracing-Metadaten, können Fehler wie "upstream sent too big header" auftreten. In diesem Fall ist proxy_buffer_size oft die zu überprüfende Direktive.

Die verbleibenden Antwortpuffer werden durch proxy_buffers gesteuert:

proxy_buffers 8 32k;

Das bedeutet acht Puffer von jeweils 32 KB für weitergeleitete Antwortdaten. Es bedeutet nicht, dass jede Anfrage von Anfang bis Ende immer den vollen Betrag verbraucht, aber Sie sollten diese dennoch als speicherbezogene Einstellungen pro Anfrage unter Last behandeln.

Ein typischer Reverse-Proxy-Block sieht so aus:

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

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

Kopieren Sie dies nicht blind. Eine kleine HTML-Site, eine JSON-API und ein Datei-Download-Dienst haben unterschiedliche Anforderungen.

Temporäre Dateien und proxy_max_temp_file_size

Wenn die Proxy-Antwortpufferung aktiviert ist und die Antwort größer als die konfigurierten Puffer ist, kann Nginx einen Teil davon auf die Festplatte schreiben. proxy_max_temp_file_size begrenzt die Größe dieser temporären Datei. Der Standardwert von Nginx wird häufig als 1024m dokumentiert, und wenn Sie ihn auf 0 setzen, wird die Pufferung von Proxy-Antworten in temporären Dateien deaktiviert.

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

Verwenden Sie 0 mit Bedacht:

proxy_max_temp_file_size 0;

Das bedeutet nicht, dass große Antworten kostenlos werden. Es bedeutet, dass Nginx keine temporären Dateien für diese gepufferten Proxy-Antworten verwendet. Abhängig von der Antwort, der Client-Geschwindigkeit und dem Pufferungsverhalten benötigen Sie möglicherweise mehr Speicher, andere Puffereinstellungen oder ein Streaming-Design.

Eine wichtige Nuance aus der Nginx-Dokumentation: Die Einschränkung proxy_max_temp_file_size gilt nicht für Antworten, die zwischengespeichert oder auf der Festplatte gespeichert werden. Wenn Sie proxy_cache oder proxy_store verwenden, überprüfen Sie diese Einstellungen separat.

Wann proxy_buffering off die bessere Antwort ist

Manchmal sollten Sie die Antwort überhaupt nicht puffern. Streaming-Endpunkte, Server-Sent Events, große Downloads und langlaufende Ausgaben funktionieren oft besser, wenn die Proxy-Pufferung deaktiviert ist:

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

Bei deaktivierter Pufferung leitet Nginx die Upstream-Antwort an den Client weiter, sobald sie eingeht, anstatt zu versuchen, die vollständige Antwort zu sammeln. Die proxy_buffer_size ist immer noch wichtig, da Nginx einen Puffer für die vom Upstream empfangenen Daten benötigt, aber proxy_buffers und das Verhalten temporärer Dateien werden weniger zentral.

Schalten Sie die Pufferung nicht global aus, es sei denn, Sie verstehen den Kompromiss. Die Pufferung schützt Upstream-Server vor langsamen Clients. Wenn Clients langsam herunterladen und die Pufferung deaktiviert ist, können Upstream-Verbindungen länger belegt bleiben.

FastCGI-Puffer für PHP und ähnliche Setups

Wenn Nginx mit PHP-FPM kommuniziert, sind die proxy_*-Direktiven nicht die, die Sie benötigen. FastCGI verwendet passende Namen:

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

    fastcgi_buffer_size 16k;
    fastcgi_buffers 8 32k;
}

Die gleiche Logik gilt: Der erste Puffer verarbeitet Header und Anfangsdaten; das Puffer-Array verarbeitet mehr Antwortinhalt. Wenn PHP große Header sendet, muss möglicherweise fastcgi_buffer_size angepasst werden. Wenn große PHP-generierte Seiten auf die Festplatte ausgelagert werden, überprüfen Sie fastcgi_buffers und die Arbeitslast.

Wie Sie optimieren, ohne zu raten

Beginnen Sie mit der Überprüfung der Protokolle:

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

Achten Sie auf Warnungen über Anforderungstexte oder Upstream-Antworten, die in temporäre Dateien gepuffert werden. Messen Sie dann die tatsächlichen Größen. Für APIs entnehmen Sie Stichproben der Anforderungs- und Antwortgrößen aus Zugriffsprotokollen, Anwendungsprotokollen oder Ihrem Observability-System. Trennen Sie für Uploads Metadatenanfragen von Datei-Upload-Endpunkten. Sie sollten nicht die gleichen Annahmen teilen.

Optimieren Sie im engsten Kontext, der sinnvoll ist. Anstelle von:

http {
    client_body_buffer_size 10m;
}

bevorzugen Sie etwas wie dies nur für den Upload-Endpunkt:

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

Für eine JSON-API mit gelegentlichen 1-MB-Antworten könnten Sie nur diesen API-Standort optimieren:

location /reports/ {
    proxy_pass http://report_backend;
    proxy_buffering on;
    proxy_buffer_size 32k;
    proxy_buffers 16 64k;
    proxy_max_temp_file_size 20m;
}

Berechnen Sie dann den schlimmsten Fall. Wenn 1.000 gleichzeitige Anfragen jeweils mehrere hundert KB an Antwortpuffern verwenden können, ist das echter Speicher. Lassen Sie Platz für Worker-Prozesse, TLS, Upstream-Verbindungen, Caches, den OS-Page-Cache und andere Dienste.

Konfiguration validieren und System überwachen

Nach jeder Änderung:

sudo nginx -t
sudo systemctl reload nginx

Überwachen Sie dann Speicher, Festplatten-E/A und Fehlerprotokolle. Ein erfolgreicher Syntax-Test beweist nur, dass die Datei geparst werden kann. Er beweist nicht, dass die Werte gut sind.

Nützliche Überprüfungen umfassen:

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

Wenn Warnungen zu temporären Dateien verschwinden, aber der Speicherdruck steigt, haben Sie übertrieben. Wenn der Speicher gesund bleibt und die Festplatten-E/A während der betroffenen Arbeitslast sinkt, hat die Änderung wahrscheinlich geholfen.

Die Nginx-Pufferoptimierung funktioniert am besten, wenn sie lokal, gemessen und an den tatsächlichen Datenverkehr gebunden ist. Halten Sie gewöhnliche Anfragen im Speicher, wenn dies die Latenz reduziert, lassen Sie wirklich große Uploads oder Downloads den richtigen Pfad verwenden und vermeiden Sie globale Einstellungen, die jede Verbindung für den größten Grenzfall bezahlen lassen.