Nginx Reverse Proxy und Load Balancing

Aus Xinux Wiki
Zur Navigation springen Zur Suche springen

Vertiefung zu Nginx Grundlagen: Location-Matching, Reverse Proxy, Load Balancing, Caching und Rate Limiting – mit den typischen Fehlerbildern. Dazu die Animationen Nginx: Grundkonfiguration (Einstieg), Nginx: Reverse Proxy (erweitert) und Nginx: Load Balancing (fortgeschritten).

Location-Matching: welche Regel gewinnt

Nginx wählt die passende location nicht nach der Reihenfolge im Config, sondern nach festen Regeln:

  1. Exact Match (location = /pfad) – trifft nur exakt, gewinnt sofort
  2. Prefix Match – der längste passende Prefix gewinnt
  3. Ist der Prefix mit ^~ markiert, stoppt hier die Prüfung – keine Regex mehr
  4. sonst: alle Regex-Locations (~ case-sensitive, ~* case-insensitive) der Reihe nach, die erste passende gewinnt – und zwar über dem Prefix
  5. ohne Regex-Treffer: Fallback auf den längsten Prefix aus Schritt 2

Der Klassiker: Regex schlägt Prefix

location /download/ {
    limit_rate 500k;                 # Downloads drosseln
}
location ~* \.(zip|iso)$ {
    limit_rate 5m;                   # war für andere Zwecke gedacht
}

Eine Anfrage nach /download/archiv.zip passt auf beide Blöcke. Da der Prefix kein ^~ hat, gewinnt die Regex-Regel – die Datei wird entgegen der Absicht nicht gedrosselt. Lösung: den Prefix mit ^~ markieren, dann gewinnt er garantiert gegen jede Regex:

location ^~ /download/ {
    limit_rate 500k;                 # gilt jetzt für alles unter /download/
}

Reverse Proxy: proxy_pass und Header

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;
}

Ohne diese Header sieht die Backend-App nur Nginx selbst (127.0.0.1) als Quelle – Client-IP, ursprünglicher Host und Protokoll gehen verloren. Wichtig: Eine App darf X-Forwarded-For nur von bekannten, kontrollierten Proxys vertrauen, sonst lässt sich der Header von jedem fälschen, der die App direkt erreicht.

Für WebSockets zusätzlich:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Load Balancing

upstream app_backend {
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
}
server {
    location / {
        proxy_pass http://app_backend;
    }
}

Verteilmethoden

Methode Verhalten
weighted round robin (Default) reihum, optional mit Gewicht je Server (weight=3)
least_conn an den Server mit den wenigsten aktiven Verbindungen
ip_hash gleiche Client-IP landet immer beim gleichen Server (Session-Affinität ohne gemeinsamen Session-Speicher, aber ungleich verteilt bei NAT/Firmennetz)

Ausfall eines Backends

upstream app_backend {
    server 10.0.0.11:3000 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:3000;
}

Das freie Nginx erkennt einen Ausfall nur passiv: Erst wenn echte Anfragen fehlschlagen, zählt das. Nach max_fails Fehlschlägen wird der Server für fail_timeout ausgeschlossen. Aktive, regelmäßige Health-Checks gibt es nur in kommerziellen oder Drittanbieter-Erweiterungen – bis max_fails erreicht ist, können einzelne Anfragen mit 502/504 beim Client landen.

Caching

# im http-Kontext
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=appcache:10m max_size=1g inactive=30m;

# in der proxied location
location / {
    proxy_pass http://app_backend;
    proxy_cache appcache;
    proxy_cache_valid 200 10m;
    add_header X-Cache-Status $upstream_cache_status always;
}

X-Cache-Status zeigt im Antwort-Header HIT/MISS/BYPASS – praktisch zur Fehlersuche. Vorsicht: Cache-Keys, Bypass-Regeln und Autorisierung sauber planen, sonst landen personalisierte Antworten (z. B. mit fremden Session-Daten) versehentlich im gemeinsamen Cache.

Rate Limiting

# im http-Kontext: Zone definieren
limit_req_zone $binary_remote_addr zone=reqs:10m rate=5r/s;

# im server- oder location-Block: anwenden
limit_req zone=reqs burst=10 nodelay;
limit_req_status 429;

burst=10 nodelay lässt kurze Spitzen bis 10 Anfragen sofort durch, statt sie zu verzögern. Wird die Rate dauerhaft überschritten, antwortet Nginx selbst mit 429 Too Many Requests – das Backend bekommt die Anfrage gar nicht erst zu sehen.

Erweitertes Routing

SPA-Fallback

location / {
    try_files $uri $uri/ /index.html;
}

Für Single-Page-Applications: Existiert die angeforderte Datei nicht, liefert Nginx stattdessen index.html aus – das clientseitige Routing im Browser übernimmt den Rest.

Named Location

location / {
    try_files $uri @backend;
}
location @backend {
    proxy_pass http://127.0.0.1:3000;
}

Eine Named Location (@name) ist nur intern erreichbar, nie direkt über eine URL – praktisch als Fallback-Ziel für try_files.

map-Direktive

map $http_upgrade $connection_upgrade {
    default upgrade;
    ""      close;
}

map erzeugt im http-Kontext eine neue Variable in Abhängigkeit vom Wert einer anderen – hier für den WebSocket-Header Connection genutzt (siehe Abschnitt „Reverse Proxy: proxy_pass und Header“).

Fehlerbilder

Code Ursache Erster Schritt
403 Dateiberechtigungen oder deny all Dateirechte/Besitzer prüfen, passende location
404 falscher root oder falscher Pfad root/alias im richtigen location-Block prüfen
413 Anfrage größer als client_max_body_size Limit gezielt erhöhen, nicht pauschal abschalten
502 Backend nicht erreichbar oder liefert ungültige Antwort Backend-Prozess/Port prüfen, error.log lesen
504 Backend antwortet, aber zu langsam proxy_read_timeout prüfen, Backend-Performance klären

Nach jeder Config-Änderung zuerst sudo nginx -t, dann erst systemctl reload nginx – ein Syntaxfehler legt sonst beim nächsten Neustart den ganzen Dienst lahm.

Links