Nginx Reverse-Proxy-Setup: Effiziente Verkehrslenkung

Richten Sie einen Nginx-Reverse-Proxy mit klarem Routing, korrekten Headern, WebSocket-Unterstützung, Timeouts, Pufferung und Fehlerbehebungsschritten ein.

Nginx Reverse-Proxy-Setup: Effiziente Verkehrslenkung

Ein Nginx-Reverse-Proxy-Setup ermöglicht es Nginx, öffentlichen Webverkehr zu empfangen und an eine oder mehrere Backend-Anwendungen weiterzuleiten. Dies ist nützlich, wenn Ihre App auf Node.js, Python, Go, Java oder einem anderen Dienst läuft, der nicht direkt dem Internet ausgesetzt werden soll.

Statt dass Benutzer eine Verbindung zu Ihrem App-Port herstellen, stellen sie eine Verbindung zu Nginx auf den Standard-HTTP- oder HTTPS-Ports her. Nginx übernimmt die öffentliche Schnittstelle und leitet den Verkehr effizient an den richtigen internen Dienst weiter.

Was ein Reverse-Proxy tut

Ein Reverse-Proxy sitzt vor Ihren Anwendungsservern. Der Client spricht mit Nginx, und Nginx spricht mit dem Backend. Für den Browser ist Nginx die Website. Für die App ist Nginx der vorgelagerte Client, es sei denn, Sie übergeben Header, die die ursprünglichen Anforderungsdetails bewahren.

Dieses Muster bietet Ihnen mehrere Vorteile:

  • Sie können Apps auf privaten Ports wie 3000, 5000 oder 8080 ausführen.
  • Sie können TLS bei Nginx terminieren.
  • Sie können verschiedene Hostnamen oder Pfade an verschiedene Dienste weiterleiten.
  • Sie können Pufferung, Timeouts, Komprimierung und Caching hinzufügen.
  • Sie können Backend-Implementierungsdetails vor dem öffentlichen Netzwerk verbergen.

Ein einfacher Reverse-Proxy für eine App, die auf 127.0.0.1:3000 läuft, sieht so aus:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

Die Direktive proxy_pass teilt Nginx mit, wohin die Anfrage gesendet werden soll. Die proxy_set_header-Zeilen bewahren nützlichen Anforderungskontext. Ohne sie protokolliert Ihre App möglicherweise jede Anfrage als von Nginx kommend und weiß möglicherweise nicht, ob die ursprüngliche Anfrage HTTP oder HTTPS verwendet hat.

Wenn Sie mit der virtuellen Host-Struktur nicht vertraut sind, lesen Sie Nginx-Serverblöcke, bevor Sie den Verkehr auf mehrere Domänen aufteilen.

Verkehrslenkung nach Host oder Pfad

Reverse-Proxy-Regeln leiten normalerweise nach Hostname, Pfad oder beidem weiter. Die hostbasierte Weiterleitung ist üblich, wenn separate Apps unterschiedliche Domänen verwenden:

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:4000;
        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;
    }
}

Die pfadbasierte Weiterleitung ist nützlich, wenn eine Domäne mehrere Dienste bedient:

server {
    listen 80;
    server_name example.com;

    location /api/ {
        proxy_pass http://127.0.0.1:4000/;
    }

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

Achten Sie auf nachgestellte Schrägstriche in proxy_pass. In Nginx können proxy_pass http://backend; und proxy_pass http://backend/; die weitergeleitete URI innerhalb eines Location-Blocks unterschiedlich umschreiben. Testen Sie die genauen URL-Pfade, die Ihre App erwartet.

Wenn beispielsweise /api/users unerwartet als /users oder /api/api/users bei Ihrem Backend ankommt, überprüfen Sie zuerst die Kombination aus Location-Präfix und nachgestelltem Schrägstrich. Dies ist einer der häufigsten Reverse-Proxy-Fehler.

Header, Timeouts und WebSockets

Header machen das Backend auf die ursprüngliche Anfrage aufmerksam. Der Host-Header ist wichtig, wenn die App absolute URLs erstellt, zulässige Hosts validiert oder mehrere Mandanten unterstützt. X-Forwarded-For hilft, die ursprüngliche Client-IP zu bewahren. X-Forwarded-Proto hilft Apps, nach der TLS-Terminierung sichere Links zu generieren.

Wenn Ihr Backend WebSockets verwendet, fügen Sie Upgrade-Header hinzu:

location /socket/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

Timeouts sollten dem Verhalten Ihrer Anwendung entsprechen. Eine normale Webanfrage sollte schnell abgeschlossen sein. Ein Berichtsexport, ein Streaming-Endpunkt oder eine Long-Polling-Anfrage benötigt möglicherweise mehr Zeit:

proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

Vermeiden Sie es, überall große Timeouts einzustellen, nur um einen langsamen Endpunkt zu verbergen. Lange Timeouts können Ressourcen binden und echte Ausfälle schwerer erkennbar machen. Passen Sie den Ort an, der es benötigt.

Pufferung ist eine weitere wichtige Einstellung. Standardmäßig kann Nginx Upstream-Antworten puffern, bevor sie an den Client gesendet werden. Dies ist für viele Web-Apps hilfreich, aber Streaming-Endpunkte benötigen möglicherweise deaktivierte Pufferung:

proxy_buffering off;

Verwenden Sie dies nur dort, wo Streaming-Verhalten erforderlich ist. Für Standard-HTML- und API-Antworten verbessert die Pufferung oft die Stabilität.

TLS-Terminierung und HTTPS-Weiterleitungen

In vielen Setups übernimmt Nginx auch HTTPS. Dadurch kann die Backend-App auf einem privaten HTTP-Port laufen, während Benutzer eine normale sichere Site auf Port 443 erhalten.

Eine übliche Form sieht so aus:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

Der Weiterleitungs-Serverblock ist absichtlich klein. Er hat eine Aufgabe: Plain-HTTP-Verkehr zu HTTPS zu verschieben. Der HTTPS-Serverblock übernimmt das Proxying.

Wenn Ihre App hinter Nginx sitzt und immer noch http://-Links generiert, überprüfen Sie, ob sie X-Forwarded-Proto vertraut. Viele Frameworks benötigen eine Einstellung wie "trust proxy" oder eine Liste zulässiger Proxys, bevor sie weitergeleitete Header verwenden. Vertrauen Sie weitergeleiteten Headern aus dem öffentlichen Internet auf Anwendungsebene nicht blind; stellen Sie sicher, dass nur Nginx den App-Port erreichen kann.

Upstream-Gruppen und einfacher Lastausgleich

Wenn ein Backend nicht ausreicht, definieren Sie eine Upstream-Gruppe:

upstream app_backend {
    server 10.0.1.10:3000;
    server 10.0.1.11:3000;
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        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;
    }
}

Open-Source-Nginx verwendet standardmäßig Round-Robin-Lastausgleich. Sie können auch Optionen wie least_conn verwenden, wenn lange Anfragen ein Backend stärker belasten als andere. Die Gesundheitsprüfung in Open-Source-Nginx ist meist passiv: Wenn ein Backend ausfällt, kann Nginx es basierend auf Fehlereinstellungen für einen Zeitraum als nicht verfügbar markieren. Nginx Plus verfügt über aktive Gesundheitschecks, aber gehen Sie nicht davon aus, dass diese Funktionen in jeder Installation vorhanden sind.

Keepalive im Upstream-Block hält Backend-Verbindungen zur Wiederverwendung offen. Dies hilft bei vielen kleinen Anfragen, aber das Backend muss in der Lage sein, die Anzahl der Leerlauf- und aktiven Verbindungen zu verarbeiten, die Nginx möglicherweise hält.

Container und private Netzwerke

Das Reverse-Proxy-Setup wird in Docker oder Kubernetes oft verwirrend, weil localhost seine Bedeutung ändert. Wenn Nginx in einem Container läuft, zeigt 127.0.0.1:3000 auf den Nginx-Container selbst, nicht auf einen separaten App-Container.

In Docker Compose proxyen Sie zum Dienstnamen:

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

In Kubernetes proxyen Sie normalerweise zu einem Service-DNS-Namen, obwohl viele Kubernetes-Bereitstellungen einen Ingress-Controller anstelle von handgeschriebenen Nginx-Serverblöcken verwenden.

Die einfache Regel lautet: Testen Sie die Konnektivität von dem Ort, an dem Nginx läuft, nicht von Ihrem Laptop und nicht vom Backend-Container. Wenn dies fehlschlägt, wird auch Nginx fehlschlagen:

curl -v http://app:3000/

Führen Sie dies innerhalb des Nginx-Containers oder auf dem Nginx-Host aus, je nach Ihrer Bereitstellung.

Sicherheitsgrenzen, die es zu überprüfen gilt

Ein Reverse-Proxy sollte die öffentliche Exposition verringern, nicht versehentlich mehr davon schaffen. Die Backend-App sollte normalerweise auf einer privaten Schnittstelle, einem privaten Subnetz oder einem Containernetzwerk lauschen. Wenn Ihre App auf 0.0.0.0:3000 auf einer öffentlichen VM lauscht, können Benutzer Nginx möglicherweise vollständig umgehen, indem sie http://example.com:3000 besuchen.

Überprüfen Sie die lauschenden Ports auf dem Host:

sudo ss -ltnp

Wenn das Backend innerhalb eines Containers auf allen Schnittstellen lauschen muss, verwenden Sie Firewall-Regeln, Sicherheitsgruppen oder Containernetzwerkeinstellungen, sodass nur Nginx es von außen erreichen kann. Dies ist wichtig, weil Apps oft auf Nginx für TLS, Anforderungsgrößenbeschränkungen, Ratenbegrenzungen, Authentifizierungs-Gateways oder IP-Zulassungslisten angewiesen sind.

Seien Sie auch vorsichtig mit weitergeleiteten Headern. Header wie X-Forwarded-For können von Clients leicht gefälscht werden, es sei denn, Nginx überschreibt sie und die App vertraut nur dem Proxy. Das übliche Nginx-Muster ist:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Dadurch wird die Client-Adresse an die Kette angehängt. Ihre Anwendung oder Protokollierungspipeline sollte wissen, welche Proxy-Adressen vertrauenswürdig sind. Andernfalls können Ratenbegrenzungen oder Audit-Logs die falsche "Client"-IP aufzeichnen.

Anforderungsgrößenbeschränkungen gehören ebenfalls in diese Diskussion. Wenn Ihre App Datei-Uploads akzeptiert, setzen Sie client_max_body_size absichtlich:

client_max_body_size 25m;

Erhöhen Sie es nicht global auf einen riesigen Wert, es sei denn, jede Route benötigt dies. Ein Profilfoto-Upload-Endpunkt und ein JSON-Login-Endpunkt sollten nicht dieselbe Anforderungskörperbegrenzung benötigen.

Eine praktische Bereitstellungs-Checkliste

Bevor Sie den Reverse-Proxy als abgeschlossen betrachten, testen Sie ihn wie ein Benutzer und wie ein Betreiber:

  • curl -I http://example.com/ sollte die erwartete Weiterleitung oder Antwort zeigen.
  • curl -I https://example.com/ sollte den erwarteten Status und die erwarteten Header zeigen.
  • Die App-Protokolle sollten den ursprünglichen Host und eine nützliche Client-IP zeigen.
  • WebSocket- oder Streaming-Endpunkte sollten separat getestet werden.
  • Ein falscher Pfad, wie /api/does-not-exist, sollte so fehlschlagen, wie Ihre App es erwartet.
  • Nginx-Fehlerprotokolle sollten bei normalen Anfragen ruhig sein.

Für das Pfad-Routing teste ich gerne drei URLs für jeden Ort: das bloße Präfix, einen normalen verschachtelten Pfad und einen Pfad mit einer Abfragezeichenfolge. Zum Beispiel:

curl -i http://example.com/api/
curl -i http://example.com/api/users
curl -i 'http://example.com/api/users?page=2'

Diese einfachen Überprüfungen fangen viele Fehler mit nachgestellten Schrägstrichen, bevor Benutzer sie bemerken.

Wenn Sie neu laden, verwenden Sie jedes Mal die gleiche sichere Sequenz:

sudo nginx -t
sudo systemctl reload nginx
sudo tail -n 50 /var/log/nginx/error.log

Wenn die App hinter der TLS-Terminierung liegt, überprüfen Sie auch, ob generierte Links, Weiterleitungen, Cookies und Callback-URLs HTTPS verwenden. Anmeldeabläufe sind oft der erste Punkt, an dem dies bricht, weil Weiterleitungen und sichere Cookies davon abhängen, dass die App das ursprüngliche Schema versteht.

Häufige Fehlermuster

502 Bad Gateway bedeutet normalerweise, dass Nginx den Reverse-Proxy-Ort erreicht hat, aber keine gültige Antwort vom Upstream erhalten konnte. Das Backend könnte ausgefallen sein, der Port könnte falsch sein, die App könnte auf einer anderen Schnittstelle lauschen oder die Verbindung könnte von einer Firewall abgelehnt werden.

504 Gateway Timeout bedeutet normalerweise, dass Nginx eine Verbindung zu etwas hergestellt hat, aber nicht rechtzeitig eine Antwort erhalten hat. Dies kann eine langsame App, eine blockierte Datenbankabfrage, ein überlasteter Worker-Pool oder ein Timeout sein, das für den Endpunkt zu kurz ist. Die Erhöhung von proxy_read_timeout kann für einen bekannten langlebigen Export-Endpunkt angemessen sein. Es ist keine Lösung für eine allgemein langsame App.

Weiterleitungsschleifen entstehen oft durch eine Diskrepanz zwischen TLS-Terminierung und den Vertrauenseinstellungen der Anwendung. Der Browser erreicht Nginx über HTTPS, Nginx leitet an die App über HTTP weiter, und die App denkt, die ursprüngliche Anfrage war Plain-HTTP. Die App leitet auf HTTPS weiter, aber das Gleiche passiert erneut. Die Übergabe von X-Forwarded-Proto ist nur die Hälfte der Lösung; die App muss ihm auch vom Proxy vertrauen.

Fehlende Client-IPs zeigen sich normalerweise darin, dass jede Anfrage von 127.0.0.1, einer Docker-Bridge-Adresse oder einer privaten Load-Balancer-Adresse kommt. Übergeben Sie X-Real-IP und X-Forwarded-For und konfigurieren Sie dann die Anwendungs- und Protokollierungsebene, um sie sicher zu lesen.

Beschädigte statische Assets nach dem Pfad-Routing entstehen oft durch Apps, die annehmen, dass sie unter / leben. Wenn Sie eine App unter /admin/ bereitstellen, generiert sie möglicherweise immer noch Links zu /assets/app.css. Sie können dies manchmal mit App-Basispfad-Einstellungen beheben. Der Versuch, jeden Asset-Pfad in Nginx umzuschreiben, ist normalerweise fragil.

Ein kleines Beispiel aus der Praxis

Stellen Sie sich eine VM vor, die drei Dienste ausführt:

  • Eine Marketing-Site auf 127.0.0.1:3000
  • Eine API auf 127.0.0.1:4000
  • Ein Admin-Tool auf 127.0.0.1:5000

Sie könnten sie so weiterleiten:

server {
    listen 443 ssl;
    server_name example.com;

    location /api/ {
        proxy_pass http://127.0.0.1:4000/;
        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 /admin/ {
        proxy_pass http://127.0.0.1:5000/;
        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 / {
        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;
    }
}

Dies kann funktionieren, hat aber Nachteile. Die API- und Admin-App müssen sich beide unter ihren Präfixen korrekt verhalten. Wenn nicht, sind separate Hostnamen wie api.example.com und admin.example.com möglicherweise sauberer. Ein gutes Reverse-Proxy-Design besteht nicht nur darin, Nginx die Konfiguration akzeptieren zu lassen; es geht darum, ein Routing zu wählen, mit dem Ihre Anwendungen leben können.

Testen und Fehlerbehebung des Setups

Testen Sie die Konfiguration immer vor dem Neuladen:

nginx -t

Laden Sie dann Nginx neu und stellen Sie eine Anfrage über den öffentlichen Hostnamen. Überprüfen Sie sowohl den Browser als auch die Protokolle. Nginx-Zugriffsprotokolle zeigen, ob die Anfrage Nginx erreicht hat. Fehlerprotokolle zeigen Verbindungsfehler, Upstream-Timeouts und Details zu Bad Gateway.

Ein praktisches Beispiel: Ihre Node.js-App läuft einwandfrei mit curl http://127.0.0.1:3000, aber die öffentliche Site zeigt 502 Bad Gateway. Das bedeutet, dass Nginx erreichbar ist, aber nicht erfolgreich mit dem Upstream kommunizieren kann. Überprüfen Sie, ob die App auf der erwarteten Adresse lauscht, ob der Port korrekt ist und ob eine lokale Firewall die Verbindung blockiert.

Häufige Reverse-Proxy-Probleme umfassen:

  • Falscher Upstream-Port oder -Adresse.
  • Backend an localhost gebunden, wenn Nginx in einem anderen Container läuft.
  • Fehlende WebSocket-Upgrade-Header.
  • App lehnt Anfragen ab, weil der Host-Header unerwartet ist.
  • Falsche URI-Umschreibung durch einen nachgestellten Schrägstrich.
  • Timeouts, die für einen langsamen Endpunkt zu kurz sind.

Bei tiefergehenden Upstream-Fehlern verwenden Sie Nginx 502 Fehlerbehebung.

Wann Sie Hilfe holen sollten

Fragen Sie einen DevOps-Ingenieur um Hilfe, wenn der Reverse-Proxy mehrere Container, private Netzwerke, TLS-Zertifikate oder lastausgeglichene Upstreams umfasst. Diese Setups können auf eine Weise fehlschlagen, die wie Nginx-Probleme aussieht, aber tatsächlich DNS-, Firewall-, Container-Netzwerk- oder Anwendungszustandsprobleme sind.

Sie sollten auch Hilfe holen, bevor Sie Admin-Panels, interne APIs oder Staging-Dienste über einen öffentlichen Reverse-Proxy bereitstellen. Kleine Routing-Fehler können ernsthafte Zugriffsprobleme verursachen.

Ein Nginx-Reverse-Proxy-Setup ist eines der nützlichsten Muster in der Web-Infrastruktur. Halten Sie das Routing klar, übergeben Sie die richtigen Header, testen Sie das Pfadverhalten sorgfältig und lassen Sie Nginx der stabile öffentliche Einstiegspunkt für Ihre Backend-Dienste sein.