SSD Nodes Learn 🎉 VPS ab $5.50/Monat
Anleitungen Matt ConnorVon Matt Connor

nginx Reverse Proxy konfigurieren: Anleitung für Ubuntu

Konfigurieren Sie einen nginx-Reverse-Proxy Zeile für Zeile: proxy_pass, vier wichtige Header, WebSockets, abschließende Slashes und Uploads unter Ubuntu 24.04.

Was eine nginx-Reverse-Proxy-Konfiguration bewirkt

Ein nginx-Reverse-Proxy nimmt die auf Port 80 und Port 443 eingehenden Anfragen entgegen und leitet jede davon an eine Anwendung weiter, die bereits auf einem lokalen Port lauscht. Anschließend gibt er die Antwort dieser Anwendung an den Browser zurück. Die Konfiguration besteht aus einem einzigen server-Block, und dieser Block ist kurz. Der größte Teil der Schwierigkeit steckt in fünf oder sechs Zeilen. Sie teilen der Anwendung mit, wer der tatsächliche Client war und welches Protokoll dieser Client verwendet hat.

Alle folgenden Schritte werden unter Ubuntu 24.04 von Grund auf mit dem nginx-Paket der Distribution aufgebaut. Ausgangspunkt ist eine Anwendung, die bereits auf 127.0.0.1:3000 antwortet. Wenn Sie sich noch nicht für einen Proxy entschieden haben, lesen Sie zuerst wie nginx im Vergleich zu Caddy und Traefik abschneidet. Danach sehen Sie Zeile für Zeile, wie die nginx-Lösung aussieht.

Führen Sie diese Konfigurationen auf Ihrem eigenen Server aus. Prüfen Sie jede Änderung vor dem Neuladen mit sudo nginx -t und lesen Sie die ausgegebene Meldung.

Wo nginx seine Konfiguration unter Ubuntu speichert

sudo apt update
sudo apt install -y nginx
ls -l /etc/nginx/sites-enabled/

Die Hauptdatei ist /etc/nginx/nginx.conf. Sie legt globale Optionen in einem http { }-Block fest und bindet anschließend zwei Verzeichnisse ein: /etc/nginx/conf.d/*.conf und /etc/nginx/sites-enabled/*. Unter Ubuntu und Debian erstellen Sie für jede Site eine eigene Datei in /etc/nginx/sites-available/ und aktivieren sie mit einem Symlink nach /etc/nginx/sites-enabled/. Wenn Sie den Symlink löschen, wird die Site deaktiviert, die Datei bleibt jedoch erhalten.

Zwei später verwendete Direktiven funktionieren nur im http-Kontext, niemals innerhalb eines server-Blocks: map und upstream. Legen Sie sie in einer eigenen Datei unter /etc/nginx/conf.d/ ab, weil dieses Verzeichnis auf der http-Ebene eingebunden wird.

Das Paket liefert eine aktivierte Site namens default mit. Sie ist als default_server gekennzeichnet. Das bedeutet, dass sie jede Anfrage beantwortet, deren Host-Header keinem server_name in Ihrer Konfiguration entspricht. Solange sie aktiviert ist, landet eine Anfrage mit einem unbekannten Hostnamen auf dieser Site statt bei Ihrer Anwendung. Entfernen Sie den Symlink, sobald Ihre eigene Site funktioniert.

sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

Der kleinste Server-Block, der eine Anwendung weiterleitet

server {
    listen 80;
    listen [::]:80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Speichern Sie ihn als /etc/nginx/sites-available/app.example.com. Aktivieren Sie ihn anschließend und laden Sie ihn.

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
curl -sI -H 'Host: app.example.com' http://127.0.0.1/

listen 80; bindet IPv4 und listen [::]:80; bindet IPv6. Lassen Sie die zweite Zeile weg, erhält ein Besucher, dessen DNS-(Domain Name System-)Auflösung für Ihren Server einen AAAA-Record liefert, eine Verbindungsablehnung. Über IPv4 funktioniert die Website dagegen. Der Fehlerbericht, den Sie erhalten, lautet: „Bei mir funktioniert es.“

server_name wird mit dem Host-Header abgeglichen, den der Browser sendet. Sie können mehrere Namen durch Leerzeichen getrennt angeben. Wenn kein Block passt, verwendet nginx den Block, der default_server ist. Deshalb musste die mitgelieferte Website entfernt werden.

location / ist eine Präfixübereinstimmung für den angeforderten Pfad, und / passt auf jeden Pfad. proxy_pass ist die Adresse, zu der nginx eine Verbindung öffnet. Lassen Sie die Anwendung an 127.0.0.1 gebunden, damit der einzige Zugangsweg über nginx führt. Wenn die Anwendung in einem Container läuft, veröffentlichen Sie sie als 127.0.0.1:3000:3000 und nicht als 3000:3000, weil Docker eigene Regeln schreibt und Ports direkt an ufw vorbei veröffentlicht. Ein unveränderter veröffentlichter Port ist daher unabhängig von den Firewall-Regeln aus dem Internet erreichbar.

Die Zeile curl sendet den korrekten Host-Header vom Server selbst. Dadurch können Sie den Block testen, bevor DNS auf ihn verweist.

Was nginx an den Upstream sendet, wenn Sie nichts weiter konfigurieren

proxy_pass allein verbirgt vier Informationen vor Ihrer Anwendung.

nginx verwendet standardmäßig HTTP/1.0 für das Backend und sendet Connection: close. Dadurch öffnet jede Anfrage eine neue Upstream-Verbindung, und ein Protokoll-Upgrade ist nicht möglich.

Der Header Host wird auf den Wert von proxy_pass umgeschrieben, also auf 127.0.0.1:3000. Eine Anwendung, die absolute Links aus Host erstellt, erzeugt dadurch Links, die niemand außerhalb des Servers öffnen kann.

Die Verbindung zur Anwendung kommt von nginx. Daher sieht die Anwendung 127.0.0.1 als Client-Adresse. Jede Protokollzeile und jedes Rate-Limit innerhalb der Anwendung erfasst dann den Proxy statt des Besuchers.

Die Anwendung kann nicht erkennen, dass der Browser HTTPS verwendet hat, weil die empfangene Verbindung über eine Loopback-Adresse und unverschlüsselt per HTTP erfolgt.

Vier Zeilen beheben all diese Probleme.

Die vier zu setzenden Header und welche Informationen jeder Header an das Backend übermittelt

location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Host übermittelt den Namen, den der Besucher eingegeben hat. $host ist der Name aus der Anfrage, ohne Port und in Kleinbuchstaben. Setzen Sie diesen Header, damit Ihre Anwendung korrekte absoluten URLs erstellt: für die Weiterleitung nach der Anmeldung oder für den Link in einer E-Mail zum Zurücksetzen des Passworts. Fehlt der Header, zeigen diese URLs auf 127.0.0.1:3000. Dann leitet die Anmeldung den Browser an eine Adresse weiter, die die Verbindung ablehnt. Wenn Ihre Anwendung auch den Port benötigt, weil sie auf 8080 bereitgestellt wird, verwenden Sie $http_host. Dieser Header entspricht exakt dem Header, den der Client gesendet hat.

X-Real-IP übermittelt einen Wert: $remote_addr, die Adresse, von der nginx die Verbindung akzeptiert hat. Anwendungen verwenden diesen Wert für ihre eigenen Zugriffsprotokolle und die eigene Ratenbegrenzung.

X-Forwarded-For übermittelt eine Liste. $proxy_add_x_forwarded_for hängt $remote_addr an den Wert an, den der Client bereits in diesem Header übermittelt hat. Der Wert ist daher durch Kommas getrennt, und der von nginx hinzugefügte Eintrag steht am Ende. Dieses Detail entscheidet darüber, ob der Header vertrauenswürdig ist: Ein Client kann beliebige X-Forwarded-For senden. Eine Anwendung, die den ersten Eintrag ausliest, kann daher auf eine beliebige Adresse getäuscht werden. Wenn nginx der Edge-Server ist, schreiben Sie stattdessen $remote_addr und verwerfen Sie die Version des Clients. Wenn ein CDN oder ein anderer Proxy vorgeschaltet ist, verwenden Sie set_real_ip_from und real_ip_header aus dem realip-Modul. Dadurch wird $remote_addr selbst zur tatsächlichen Clientadresse.

X-Forwarded-Proto übermittelt http oder https. Frameworks lesen diesen Wert aus, um zu entscheiden, ob Cookies mit Secure markiert werden und ob eine Weiterleitung zu HTTPS erzwungen wird. Fehlt der Header auf einer TLS-Site und ist die Anwendung so konfiguriert, dass sie HTTPS erzwingt, sieht sie http. Sie antwortet mit einer Weiterleitung an die HTTPS-Adresse, empfängt die nächste Anfrage über nginx, sieht weiterhin http und leitet erneut weiter. Der Browser gibt auf und zeigt ERR_TOO_MANY_REDIRECTS an.

Wenn Sie diese vier Zeilen in jeder location wiederholen, driften sie mit der Zeit auseinander. Legen Sie sie in einer Datei ab und binden Sie diese ein.

# /etc/nginx/snippets/proxy-headers.conf
proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location / {
    include snippets/proxy-headers.conf;
    proxy_pass http://127.0.0.1:3000;
}

Die Vererbung hat hier eine Besonderheit. Eine location erbt die Direktiven proxy_set_header aus ihrem server-Block nur, solange sie keine eigenen Direktiven dieser Art definiert. Fügen Sie eine einzelne proxy_set_header innerhalb der location hinzu, werden alle auf Serverebene definierten Header für diese location verworfen. Halten Sie daher alle Header auf einer Ebene oder include das Snippet in jeder location ein, die als Proxy dient.

Warum verbindet sich meine WebSocket-Anwendung und trennt die Verbindung dann wieder?

Die Standardwerte verhindern das Upgrade. Außerdem schließt das standardmäßige Lesetimeout einen inaktiven Tunnel nach 60 Sekunden. Ein WebSocket beginnt als HTTP-Anfrage mit Upgrade: websocket und Connection: Upgrade. Dabei handelt es sich um Hop-by-Hop-Header. Ein Proxy soll diese Header daher verarbeiten, statt sie weiterzuleiten. HTTP/1.0 verfügt außerdem überhaupt nicht über einen Upgrade-Mechanismus. Beide Header müssen manuell wieder gesetzt werden.

Die Map gehört in den http-Kontext und in eine eigene Datei.

# /etc/nginx/conf.d/websocket.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Danach folgt der Location-Block.

location / {
    include snippets/proxy-headers.conf;
    proxy_pass http://127.0.0.1:3000;

    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

Die Map ermöglicht es, beide Arten von Datenverkehr über einen Location-Block bereitzustellen. Bei einer normalen Anfrage ist $http_upgrade leer. Daher wird $connection_upgrade zu close. Bei einer Upgrade-Anfrage enthält es websocket. Der an das Backend gesendete Header lautet dann Connection: upgrade. Wenn proxy_set_header Connection "upgrade"; fest eingetragen wird, wird der Header auch bei jeder normalen Seitenanfrage gesendet. Einige Backends beantworten eine solche Anfrage mit 400.

proxy_read_timeout verursacht die Meldungen „Die Seite wird geladen und aktualisiert sich dann nicht mehr“. Der Standardwert beträgt 60 Sekunden. Gemessen wird der Abstand zwischen zwei Lesevorgängen vom Backend, nicht die Lebensdauer der Verbindung. Ein WebSocket, der 60 Sekunden lang keine Daten überträgt, wird von nginx geschlossen. Die Browserkonsole zeigt dann, dass der Socket mit dem Code 1006 geschlossen wurde. Anwendungen, die häufiger als einmal pro Minute eigene Heartbeats senden, bemerken das nicht. Anwendungen ohne solche Heartbeats fallen exakt nach einer Minute aus. Bei Live-Editoren und Dashboards tritt das Problem zuerst auf. Eine selbst gehostete n8n-Instanz hinter HTTPS ist ein häufiges Beispiel.

Warum verändert ein abschließender Schrägstrich in proxy_pass meine URLs?

Die Regel lässt sich in einem Satz zusammenfassen. Wenn proxy_pass mit einer URI (Uniform Resource Identifier) endet, selbst mit einer einfachen /, entfernt nginx den Teil des Anfragepfads, der dem location-Präfix entspricht, und setzt diese URI an seine Stelle. Wenn proxy_pass beim Host und Port endet, wird der Anfragepfad unverändert weitergegeben.

location /app/ {
    proxy_pass http://127.0.0.1:3000/;
}

Eine Anfrage an /app/status erreicht das Backend als /status.

location /app/ {
    proxy_pass http://127.0.0.1:3000;
}

Eine Anfrage an /app/status erreicht das Backend als /app/status.

Welche Form Sie benötigen, hängt von der Anwendung ab. Eine Anwendung mit einer Einstellung für Basispfad oder Unterverzeichnis benötigt die zweite Form. Die Einstellung muss dabei über /app informiert sein. Eine Anwendung, die Präfixe nicht kennt, benötigt die erste Form. Die erste Form hat einen sofort sichtbaren Nachteil: Das von der Anwendung zurückgegebene HTML enthält weiterhin absolute Pfade wie /static/main.css. Der Browser fordert diese am Root der Website an. Keine location passt darauf, und die Seite wird ohne Formatierung dargestellt. Im Netzwerk-Tab des Browsers werden diese Asset-Anfragen mit 404 beantwortet. Die Lösung ist die eigene Basispfad-Einstellung der Anwendung oder eine zweite location /static/, die auf dasselbe Backend verweist.

Eine Regex-location kann in proxy_pass keine URI enthalten. sudo nginx -t lehnt die Konfiguration ab und nennt den Grund: "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block.

Diese gesamte Problemklasse entfällt, wenn jede Anwendung einen eigenen Namen, app.example.com, erhält und über location / per Proxy weitergeleitet wird. Unterpfade sind den zusätzlichen Aufwand nur dann wert, wenn Sie keine DNS-Einträge hinzufügen können.

Wie stelle ich mehrere Backends hinter einem Namen bereit?

Mit einem upstream-Block. Er gehört in den http-Kontext. Schreiben Sie ihn daher oberhalb des server-Blocks in derselben Datei oder in /etc/nginx/conf.d/.

upstream app_backend {
    least_conn;
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

Die Location verweist anschließend darauf: proxy_pass http://app_backend;.

Die Standardmethode ist Round-Robin. least_conn sendet jede Anfrage an das Backend mit den wenigsten aktiven Verbindungen. Das eignet sich für Anfragen mit unterschiedlich langer Laufzeit. ip_hash bindet eine Client-Adresse an ein Backend. ip_hash ist erforderlich, wenn die Anwendung Sitzungen im eigenen Speicher hält. Round-Robin über zwei solche Backends meldet Benutzer zufällig ab, sobald ihre Anfragen bei der Instanz ankommen, die sie noch nicht gesehen hat. Sitzungen in einem gemeinsamen Speicher abzulegen, ist die bessere Lösung.

max_fails=3 fail_timeout=30s bedeutet, dass drei fehlgeschlagene Versuche innerhalb von 30 Sekunden den Server für 30 Sekunden aus dem Pool nehmen. Wenn sich jeder Server im Block in diesem Zustand befindet, erhalten Clients den Status 502, und das Fehlerprotokoll enthält no live upstreams while connecting to upstream.

keepalive 32 hält pro Worker-Prozess bis zu 32 inaktive Verbindungen zu den Backends offen. Dadurch entfällt bei den meisten Anfragen der TCP-Handshake. Das funktioniert nur mit proxy_http_version 1.1 und wenn keine Connection: close-Direktive an das Upstream gesendet wird. Wenn dieselbe Location auch die WebSocket-Zuordnung verwendet, ändern Sie den leeren Fall von close in eine leere Zeichenfolge. Dann enthalten normale Anfragen keinen Connection-Header, und die Verbindung aus dem Pool kann wiederverwendet werden.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      '';
}

Namen innerhalb eines upstream-Blocks werden beim Start von nginx aufgelöst. Wenn Ihr Backend ein Container ist, der nach einem Neustart eine neue Adresse erhält, verwendet nginx weiterhin die alte Adresse, bis Sie nginx neu laden. Innerhalb eines Docker-Netzwerks können Sie die Auflösung mit dem integrierten Resolver auf den Anfragezeitpunkt verschieben.

resolver 127.0.0.11 valid=10s;
set $backend http://app:3000;
proxy_pass $backend;

Wenn Container so häufig erstellt und entfernt werden, dass Sie nginx laufend anpassen müssen, ist ein Proxy die bessere Lösung, der Container-Labels ausliest. Traefik vor mehreren Docker-Compose-Anwendungen erstellt seine Routen direkt aus den Containern.

Warum schlagen Uploads mit 413 Request Entity Too Large fehl?

client_max_body_size ist standardmäßig auf 1 megabyte gesetzt. nginx weist einen größeren Request-Body zurück, bevor Ihre Anwendung ihn erhält. Im Error-Log wird client intended to send too large body protokolliert. Erhöhen Sie den Wert im Server-Block oder in dem location-Block, in dem die Uploads stattfinden.

client_max_body_size 512m;

Der Wert 0 deaktiviert die Prüfung vollständig. Die Anwendung hat ebenfalls ein eigenes Limit. Wenn der Fehler 413 nach dieser Änderung weiterhin auftritt, stammt er vom Backend. Prüfen Sie als Nächstes die Upload-Einstellung der Anwendung.

Standardmäßig liest nginx den vollständigen Request-Body ein, bevor die Verbindung zum Upstream geöffnet wird. Größere Datenmengen werden vorher in eine temporäre Datei auf der Festplatte geschrieben. Dadurch wird die Anwendung vor langsamen Clients geschützt, weil das Backend den Upload mit voller lokaler Geschwindigkeit erhält. Bei sehr großen Uploads können Sie stattdessen Streaming aktivieren.

proxy_request_buffering off;

Das Backend erhält den Body dann, sobald er eintrifft, und muss ihn entsprechend verarbeiten können. nginx kann den Request außerdem nicht mehr gegen einen anderen Upstream wiederholen, weil der Body bereits nicht mehr verfügbar ist.

client_body_timeout beträgt standardmäßig 60 Sekunden. Der Wert gilt zwischen zwei aufeinanderfolgenden Lesevorgängen des Bodys und nicht für den gesamten Upload. Ein langsamer, aber kontinuierlicher Upload wird dadurch nicht abgebrochen. Bei einem Stillstand wird die Verbindung beendet.

Pufferung von Antworten und die Einstellung, die Live-Ausgaben beeinträchtigt

proxy_buffering ist standardmäßig aktiviert und normalerweise die richtige Einstellung. nginx liest die Antwort so schnell aus Ihrer Anwendung, wie diese sie schreiben kann, hält sie vor und liefert sie in der Geschwindigkeit eines langsamen Clients aus. Der Worker der Anwendung ist dadurch frühzeitig fertig, statt während des gesamten langsamen Downloads belegt zu bleiben.

Dadurch werden Streaming-Antworten beeinträchtigt. Server-Sent Events und die Live-Ausgabe von Logs zeigen dem Leser nichts an, bis ein Puffer gefüllt ist. Deaktivieren Sie die Pufferung nur an diesem Ort.

proxy_buffering off;

Wenn Sie die Anwendung kontrollieren, ist es besser, bei den Streaming-Antworten ausschließlich den Header X-Accel-Buffering: no zu senden. nginx wertet diesen Header für jede Antwort aus und deaktiviert die Pufferung nur für diese Antwort. Normale Seiten behalten dadurch den Vorteil der Pufferung.

Wenn das Fehlerprotokoll upstream sent too big header while reading response header from upstream meldet, haben die Antwort-Header nicht in einen einzelnen Puffer gepasst. proxy_buffer_size ist standardmäßig auf eine einzelne Speicherseite eingestellt, je nach Plattform auf 4 oder 8 Kilobyte. Lange Cookies oder große Authentifizierungs-Header können diesen Puffer überschreiten. Erhöhen Sie beide Werte.

proxy_buffer_size 16k;
proxy_buffers 8 16k;

Wo gehört TLS in diese Konfiguration?

Bei nginx, vor alle oben genannten Komponenten. TLS (Transport Layer Security) wird am Proxy terminiert. Die Verbindung von nginx zur Anwendung bleibt über die Loopback-Adresse unverschlüsselt. Kein anderer Host im Netzwerk kann sie mitlesen. Die Anwendung erkennt anhand von X-Forwarded-Proto, dass der Besucher HTTPS verwendet hat. Das ist der vierte der vier Header.

Tragen Sie Zertifikatspfade nicht manuell ein. Verweisen Sie mit dem DNS-Eintrag auf den Server, öffnen Sie die Firewall und lassen Sie Certbot diesen Server-Block bearbeiten. Certbot fügt die Zeile listen 443 ssl mit den Pfaden ssl_certificate sowie eine Weiterleitung von Port 80 hinzu. Ein Let's-Encrypt-Zertifikat für nginx mit Certbot ausstellen beschreibt die Ausstellung und den Erneuerungs-Timer.

sudo ufw allow 'Nginx Full'
sudo ufw status

Nginx Full ist ein Anwendungsprofil, das das nginx-Paket installiert. Es öffnet Port 80 und Port 443 gemeinsam. Port 80 muss für die HTTP-01-Erneuerungs-Challenge geöffnet bleiben, auch wenn jeder Besucher anschließend zu HTTPS weitergeleitet wird.

Konfiguration testen und anschließend neu laden

sudo nginx -t
sudo systemctl reload nginx

nginx -t analysiert jede eingebundene Datei und meldet entweder einen erfolgreichen Test oder gibt die Datei und die Zeile aus, an der die Verarbeitung beendet wurde. Lesen Sie diese Ausgabe, bevor Sie die Konfiguration neu laden. Ein Reload mit einer fehlerhaften Konfiguration wird nicht angewendet: nginx verwendet weiterhin die vorherige Konfiguration. Die Website bleibt dadurch erreichbar, während Ihre Änderung stillschweigend wirkungslos bleibt. systemctl restart verhält sich anders und problematischer, weil ein Neustart den laufenden Server zuerst beendet. Ein Konfigurationsfehler führt dann dazu, dass nginx überhaupt nicht mehr läuft. Verwenden Sie standardmäßig einen Reload. Nutzen Sie restart nur für seltene Änderungen, die dies erfordern.

sudo tail -f /var/log/nginx/error.log
sudo ss -lntp | grep -E ':(80|443|3000)'

Die Zeile ss zeigt, welcher Prozess welchen Port belegt. Damit können Sie bestätigen, dass die Anwendung tatsächlich dort auf Verbindungen wartet, wohin proxy_pass verweist.

Die Fehler, die tatsächlich auftreten

502 Bad Gateway mit connect() failed (111: Connection refused) while connecting to upstream im Fehlerprotokoll. Unter der in proxy_pass angegebenen Adresse lauscht kein Dienst. Die Anwendung ist beendet, an einen anderen Port gebunden oder an eine nur innerhalb des Containers erreichbare Adresse gebunden.

502 mit no live upstreams while connecting to upstream. Jeder Server im upstream-Block ist von max_fails derzeit als fehlgeschlagen markiert. Reparieren Sie die Backends. nginx versucht sie erneut, sobald fail_timeout abgelaufen ist.

504 Gateway Time-out mit upstream timed out (110: Connection timed out) while reading response header from upstream. Das Backend hat die Verbindung angenommen und anschließend für proxy_read_timeout Sekunden keine Daten gesendet. Das Erhöhen des Timeouts ist bei einem tatsächlich langsamen Bericht richtig, bei einer festhängenden Anwendung jedoch falsch.

Jeder Pfad liefert von der Anwendung 404 zurück. Die Regel für den abschließenden Schrägstrich hat den Pfad umgeschrieben. Vergleichen Sie den Pfad im Anwendungsprotokoll mit dem angeforderten Pfad.

Eine andere Website antwortet. server_name stimmt nicht mit dem Host-Header überein. Daher ist die Anfrage in den default_server-Block gefallen.

Die Seite wird geladen, anschließend friert die Oberfläche nach etwa einer Minute ein. Das ist der WebSocket-Fall: Die Upgrade-Verarbeitung fehlt oder proxy_read_timeout steht weiterhin auf 60 Sekunden.

FAQ

Warum gibt nginx nach dem Hinzufügen von proxy_pass den Fehler 502 Bad Gateway zurück?

nginx konnte keine Verbindung zur Adresse in proxy_pass herstellen. Das Fehlerprotokoll unter /var/log/nginx/error.log nennt die Ursache: connect() failed (111: Connection refused) while connecting to upstream bedeutet, dass dort kein Prozess Verbindungen annimmt, und no live upstreams bedeutet, dass jeder Server in einem upstream-Block als fehlgeschlagen markiert wurde. Führen Sie sudo ss -lntp | grep 3000 aus, um zu prüfen, welcher Prozess den Port belegt und an welche Adresse er gebunden ist. Eine Anwendung, die an eine nur innerhalb des Containers erreichbare Adresse oder an einen anderen als den angegebenen Port gebunden ist, verursacht diesen Fehler jedes Mal.

Warum trennt meine Anwendung hinter nginx nach etwa einer Minute die Verbindung?

Bei der Verbindung handelt es sich um einen WebSocket, und proxy_read_timeout steht noch auf dem Standardwert von 60 Sekunden. Dieser Wert misst den Abstand zwischen zwei Lesevorgängen vom Backend. Einen inaktiven Socket schließt nginx, und die Browserkonsole meldet den Schließcode 1006. Setzen Sie proxy_http_version 1.1, leiten Sie Upgrade und Connection mit einem map auf $http_upgrade weiter und erhöhen Sie proxy_read_timeout auf einen Wert wie 3600s. Ohne den Header Upgrade wird das Upgrade überhaupt nicht durchgeführt. Die Anwendung fällt dann auf Polling zurück oder zeigt keine Live-Aktualisierungen an.

Ist der abschließende Schrägstrich in proxy_pass relevant?

Ja. Er ändert den Pfad, den das Backend empfängt. Mit location /app/ und proxy_pass http://127.0.0.1:3000/ kommt eine Anfrage für /app/status beim Backend als /status an, weil jeder URI-Teil nach Host und Port das Präfix des gefundenen location-Blocks ersetzt. Entfernen Sie den abschließenden Schrägstrich, kommt dieselbe Anfrage als /app/status an. Das Entfernen des Präfixes führt häufig dazu, dass die eigenen Asset-Links der Anwendung nicht mehr funktionieren. Diese bleiben absolut und liefern dann am Stammverzeichnis der Website den Status 404 zurück. Bei einer Anwendung mit einer Einstellung für den Basispfad ist daher die Form besser geeignet, die den Pfad unverändert weitergibt.

Warum protokolliert meine Anwendung 127.0.0.1 als IP-Adresse jedes Besuchers?

Die Verbindung, die die Anwendung empfängt, kommt tatsächlich von nginx über die Loopback-Adresse. Die Adresse des Besuchers erreicht die Anwendung nur über einen von Ihnen gesetzten Header: proxy_set_header X-Real-IP $remote_addr; für einen einzelnen Wert und proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; für die angehängte Kette. Anschließend muss die Anwendung so konfiguriert werden, dass sie diesen Headern vertraut. Beachten Sie, dass ein Client seinen eigenen X-Forwarded-For senden kann. Wenn nginx der Edge-Server ist, überschreiben Sie diesen Header daher mit $remote_addr, statt ihn anzuhängen.

Benötige ich TLS für die Verbindung zwischen nginx und meiner Anwendung?

Nicht, wenn die Anwendung auf demselben Server läuft und an 127.0.0.1 gebunden ist. Dieser Datenverkehr verlässt den Rechner dann nicht. Beenden Sie TLS in nginx, verwenden Sie proxy_pass für unverschlüsseltes HTTP über die Loopback-Schnittstelle und senden Sie X-Forwarded-Proto $scheme, damit die Anwendung weiß, dass der Besucher HTTPS verwendet hat. Wenn sich das Backend auf einem anderen Host in einem Netzwerk befindet, das Sie nicht kontrollieren, benötigt diese Verbindung einen eigenen Schutz. Verwenden Sie dafür entweder HTTPS zum Backend oder einen privaten Tunnel zwischen den beiden Rechnern.