Nginx Port Forwarding - So proxy't man richtig!

Diagramm zeigt einen Client, der eine HTTP-Anfrage an einen Docker-Host mit nginx-proxy (Port 80:80) sendet. Der nginx-proxy versucht, Anfragen an Backend-Container weiterzuleiten, aber die Verbindungen sind unterbrochen. Dies illustriert ein Problem m...

Geschrieben von

Enno Wendt

Veröffentlicht am

29. Mai 2026

Inhaltsverzeichnis

Mit einer sauberen Weiterleitung über Nginx lässt sich ein interner Dienst hinter einem öffentlichen Port verstecken, ohne dass man die Kontrolle über Header, TLS oder Zielsysteme verliert. Bei nginx port forwarding ist in der Praxis meist eine Weiterleitung auf HTTP-, TCP- oder UDP-Ebene gemeint, nicht klassisches NAT auf Router-Ebene. Genau diese Unterscheidung ist wichtig, weil davon abhängt, ob du mit `proxy_pass` im HTTP-Kontext arbeitest oder das `stream`-Modul brauchst.

Die wichtigsten Punkte auf einen Blick

  • Nginx leitet Verbindungen nicht wie ein Router per NAT weiter, sondern proxy't sie auf HTTP-, TCP- oder UDP-Ebene.
  • Für Webanwendungen reicht meist `proxy_pass` im HTTP-Kontext, für Protokolle wie DNS oder PostgreSQL brauchst du `stream`.
  • Der Host-Header, `X-Forwarded-For` und die richtige `listen`-Konfiguration entscheiden oft darüber, ob die Weiterleitung sauber funktioniert.
  • Das `stream`-Modul ist für rohe Verbindungen sehr praktisch, aber dort gibt es keine HTTP-Header und keine Pfadlogik.
  • Die häufigsten Fehler sind falsche `proxy_pass`-Pfade, fehlende Module und nicht freigegebene Ports in Firewall oder Backend.

Warum nginx port forwarding in der Praxis meist Reverse Proxy bedeutet

Ich trenne das Thema bewusst in zwei Ebenen: Nginx kann HTTP-Anfragen an eine interne Anwendung weiterreichen oder rohe TCP/UDP-Verbindungen durchreichen. Nginx macht dabei kein NAT; er nimmt Verbindungen auf einem öffentlichen Port an und spricht sie kontrolliert an ein Backend weiter. Genau deshalb schaue ich zuerst auf `listen`, `server_name` und `proxy_pass`, bevor ich überhaupt an Feinjustierung denke.

Für Webserver ist das meist ein Reverse-Proxy-Szenario: außen etwa Port 80 oder 443, innen ein Dienst auf `127.0.0.1:3000` oder auf einer privaten IP. Nginx entscheidet dabei zunächst anhand von IP und Port, welcher `server`-Block zuständig ist, und prüft danach den `Host`-Header. Wenn die Zuordnung nicht passt, landet die Anfrage schnell im falschen virtuellen Host oder im Default-Server. Das ist der Grund, warum eine saubere Host-Konfiguration oft mehr wert ist als jede später gefrickelte Rewrite-Regel.

Wer diese Logik versteht, baut stabiler und spart sich die meisten späteren Überraschungen. Als Nächstes lohnt sich der Blick darauf, wann HTTP genügt und wann ich auf das Stream-Modul wechseln würde.

Schema zeigt, wie Nginx Port Forwarding Anfragen von einem Client an einen Apache Backend-Server weiterleitet.

Wann HTTP reicht und wann das Stream-Modul nötig wird

Die eigentliche Weiche ist einfach: Sprichst du mit einer Webanwendung, reicht meistens der HTTP-Kontext. Geht es um ein Protokoll ohne HTTP-Struktur, brauchst du `stream`. Genau an dieser Stelle werden viele Setups unnötig kompliziert, weil HTTP und TCP in einen Topf geworfen werden.

Szenario Passender Nginx-Modus Warum das sinnvoll ist Worauf ich achte
Website, API, Admin-Panel HTTP Reverse Proxy Nginx kann Header, Pfade, Cookies und TLS sauber behandeln. `proxy_set_header`, `server_name`, Redirects
WebSocket oder SSE HTTP Reverse Proxy Bleibt im HTTP-Kontext, braucht aber oft Upgrade-Header. Verbindungslaufzeit, Upgrade-Weitergabe, Timeouts
PostgreSQL, Redis, MQTT, SSH TCP über `stream` Es gibt keine HTTP-Header, nur eine rohe Verbindung. Portzuordnung, `proxy_timeout`, Modulaktivierung
DNS oder andere UDP-Dienste UDP über `stream` UDP braucht ein anderes Session-Verhalten als HTTP. `listen ... udp`, Timeout, Backend-Erreichbarkeit

Praktisch heißt das auch: Das `stream`-Modul ist nicht automatisch in jeder Installation aktiv. Bei eigenen Builds muss es mit `--with-stream` mitkompiliert werden, bei Paketinstallationen ist es oft als Modul vorhanden, aber nicht zwingend geladen. Wenn ich das nicht zuerst prüfe, suche ich mich später unnötig dumm und dämlich. Mit dieser Trennung im Kopf wird die eigentliche Konfiguration deutlich klarer.

Eine saubere HTTP-Weiterleitung mit proxy_pass aufbauen

Für Webanwendungen ist das der Fall, den ich am häufigsten einsetze. Das Backend läuft auf einem internen Port, Nginx nimmt den öffentlichen Traffic an und leitet die Anfragen weiter. Wichtig ist dabei nicht nur das Ziel, sondern auch, welche Informationen die Zielanwendung bekommt.

Minimalbeispiel für eine Webanwendung

server {
    listen 80;
    server_name app.example.de;

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

Ich lasse das Backend hier absichtlich nur auf `127.0.0.1` hören. So ist der Dienst nicht direkt aus dem Netz erreichbar, und Nginx bleibt die einzige öffentliche Eintrittsstelle. `proxy_set_header Host $host;` ist dabei kein kosmetisches Detail, sondern verhindert, dass die Anwendung nur den internen Upstream-Namen sieht. `X-Forwarded-For` und `X-Real-IP` helfen bei Logs, Rate-Limits und Fehlersuche, weil die eigentliche Client-Adresse erhalten bleibt.

Der kleine Slash-Unterschied mit großer Wirkung

Ein typischer Stolperstein steckt im Pfad hinter `proxy_pass`. Diese beiden Varianten verhalten sich nicht gleich:

  • `location /app/ { proxy_pass http://127.0.0.1:3000/; }` entfernt den Prefix `/app/` und reicht den Rest weiter.
  • `location /app/ { proxy_pass http://127.0.0.1:3000; }` reicht die vollständige URI weiter.

Dieser Unterschied ist klein, aber in der Praxis oft die Ursache für 404s, kaputte Assets oder seltsame Redirects. Wenn eine Anwendung WebSockets nutzt, ergänze ich außerdem die Upgrade-Header, damit der Tunnel nicht unterwegs abbricht. Gerade bei mehreren Diensten auf derselben Maschine ist dieses Detail oft der Unterschied zwischen sauberer Entkopplung und einem langen Nachmittag mit Logs.

TCP und UDP über stream weiterreichen

Sobald es nicht mehr um HTTP geht, wechsle ich in den `stream`-Kontext. Dort arbeitet Nginx als generischer TCP- oder UDP-Proxy, also auf der Transportschicht. Es gibt keine Pfadregeln, keine Header und keine `server_name`-Logik wie im Web-Kontext.

Lesen Sie auch: Nginx DNS Resolver - Wann er wirklich nötig ist und wie er funktioniert

Beispiel für einen TCP-Dienst

stream {
    upstream postgres_backend {
        server 127.0.0.1:5432;
    }

    server {
        listen 15432;
        proxy_connect_timeout 1s;
        proxy_timeout 10m;
        proxy_pass postgres_backend;
    }

    server {
        listen 53 udp;
        proxy_timeout 20s;
        proxy_pass 127.0.0.1:5353;
    }
}

Das erste Beispiel leitet einen externen Port auf eine interne PostgreSQL-Instanz weiter. Das zweite zeigt, wie sich auch UDP-Dienste wie DNS proxy'en lassen. Für beide Fälle gilt: Das Zielsystem sollte möglichst nur intern erreichbar sein, sonst baust du dir ungewollt zwei öffentliche Zugänge zum selben Dienst.

Wenn der Backend-Dienst die Client-Adresse kennen muss, ist das PROXY-Protocol der übliche Weg, sofern die Gegenseite es versteht. Ich setze das aber nur ein, wenn ich den kompletten Pfad kontrolliere, weil sonst schnell Kompatibilitätsprobleme entstehen. Damit sind die sauberen Konfigurationsfälle abgedeckt; im Alltag scheitert es dann meist an denselben wenigen Fehlern.

Typische Fehler, die ich in der Praxis immer wieder sehe

Symptom Wahrscheinliche Ursache Sauberer Fix
502 Bad Gateway Backend läuft nicht, falscher Port oder falsche IP Dienst prüfen, Portbindung prüfen, mit `ss` oder Logs gegenprüfen
404 trotz funktionierendem Proxy Falscher `location`-Block oder falsches `proxy_pass`-Pfadverhalten Trailing Slash kontrollieren und URI-Rewrite bewusst setzen
Backend sieht den falschen Host `Host`-Header wird nicht explizit weitergereicht `proxy_set_header Host $host;` setzen
Konfiguration greift nicht Direktive steht im falschen Kontext oder `stream` fehlt Modul laden, Blockstruktur prüfen, `nginx -t` laufen lassen
Port ist bereits belegt Nginx und Anwendung binden denselben öffentlichen Port Backend auf internen Port legen, Nginx bleibt vorn
WebSocket trennt sich sofort Upgrade-Header oder Timeouts fehlen Upgrade-Header ergänzen und Timeout passend erhöhen

Vor jedem Reload prüfe ich die Konfiguration mit `nginx -t`. Das klingt banal, spart aber genau die Fehler, die man nachts am wenigsten gebrauchen kann: einen halbfertigen Reload, einen stillen Syntaxfehler oder einen Block, der zwar formal gültig ist, aber logisch nie erreicht wird. Wenn der Syntaxcheck sauber ist, gehe ich erst in den Betrieb über.

So plane ich eine robuste Weiterleitung für den Betrieb

Wenn ich eine Weiterleitung nicht nur zum Testen, sondern für den echten Betrieb aufsetze, denke ich in Schichten. Nginx soll die öffentliche Kante sein, das Backend bleibt intern, und der Verkehr zwischen beiden Seiten wird so einfach wie möglich gehalten. Je weniger Dienste direkt nach außen sprechen, desto kleiner wird die Angriffsfläche.

  • Ich binde interne Dienste an `127.0.0.1` oder an ein privates Netz, nicht an `0.0.0.0`.
  • Ich terminierte TLS möglichst an Nginx, wenn ich Zertifikate zentral verwalten will.
  • Ich trenne Dienste über eigene `server`-Blöcke oder `upstream`-Gruppen, statt alles in einen Block zu quetschen.
  • Ich passe Timeouts an, wenn Uploads, lange API-Aufrufe oder WebSockets im Spiel sind.
  • Ich prüfe, ob Firewall-Regeln nur die wirklich benötigten Ports freigeben.

Für einfache Web-Apps ist das meist die beste Lösung: ein Port nach außen, ein sauberer Proxy davor, ein oder mehrere Backends dahinter. Für reine Admin-Protokolle oder komplexe Netzgrenzen würde ich dagegen eher mit VPN, Bastion Host oder Firewall-Regeln arbeiten, statt Nginx zur Allzwecklösung zu machen. Genau an dieser Stelle trennt sich eine brauchbare von einer nur halb verstandenen Architektur.

Was ich vor dem Go-live noch einmal prüfe

  • Hört Nginx wirklich auf dem gewünschten öffentlichen Port?
  • Antwortet das Backend nur intern und nicht direkt im Netz?
  • Passen `Host`, `X-Forwarded-For` und `X-Forwarded-Proto` zur Anwendung?
  • Funktionieren Redirects, WebSockets und lange Requests ohne Abbrüche?
  • Ist `stream` geladen, falls TCP oder UDP weitergeleitet werden soll?
  • Sind Firewall, Hosting-Policy und eventuelle Systemregeln an der richtigen Stelle geöffnet?

Wenn ich nur einen Rat mitgeben würde, dann diesen: Richte die Weiterleitung so ein, dass Nginx die einzige öffentliche Eintrittsstelle ist, und teste jede Route einmal mit einem echten Client statt nur mit einer lokalen Shell. Genau dort zeigen sich Probleme mit Headern, Pfaden und Timeouts am schnellsten.

Häufig gestellte Fragen

Nginx Port Forwarding ist ein Reverse Proxy auf HTTP-, TCP- oder UDP-Ebene, kein klassisches NAT auf Router-Ebene. Nginx nimmt Verbindungen an und leitet sie kontrolliert an Backends weiter, während NAT IP-Adressen umschreibt.

Für Webanwendungen (HTTP/HTTPS) nutzen Sie `proxy_pass` im HTTP-Kontext. Für Protokolle ohne HTTP-Struktur wie PostgreSQL oder DNS verwenden Sie das `stream`-Modul für rohe TCP- oder UDP-Weiterleitungen.

Typische Fehler sind: Backend nicht erreichbar (502), falsche Pfade (`location`/`proxy_pass`), fehlende `Host`-Header, falscher Kontext für Direktiven oder nicht geladene Module (z.B. `stream`). Immer `nginx -t` nutzen!

Das Binden interner Dienste an `127.0.0.1` (localhost) oder ein privates Netz stellt sicher, dass sie nicht direkt aus dem Internet erreichbar sind. Nginx bleibt die einzige öffentliche Eintrittsstelle und erhöht die Sicherheit.

Bei WebSockets oder langen Verbindungen im HTTP-Kontext müssen Sie die Upgrade-Header korrekt weitergeben (`proxy_set_header Upgrade $http_upgrade;`) und die Timeouts entsprechend anpassen, um Verbindungsabbrüche zu vermeiden.

Artikel bewerten

Bewertung: 0.00 Stimmenanzahl: 0

Tags:

nginx port forwarding nginx port forwarding konfiguration nginx tcp udp proxy nginx stream modul nginx reverse proxy best practices

Beitrag teilen

Enno Wendt

Enno Wendt

Mein Name ist Enno Wendt und ich arbeite seit 7 Jahren im Bereich IT-Infrastruktur, Web-Technologien und Sicherheit. Mein Interesse an diesen Themen begann früh, als ich die Möglichkeiten erkannte, die Technologie bietet, um Probleme zu lösen und Prozesse zu optimieren. Ich finde es spannend, komplexe technische Zusammenhänge verständlich zu erklären und dabei aktuelle Trends und Entwicklungen im Blick zu behalten. In meinen Beiträgen konzentriere ich mich darauf, nützliche und präzise Informationen bereitzustellen, die sowohl für Fachleute als auch für Einsteiger zugänglich sind. Ich lege großen Wert darauf, meine Quellen sorgfältig zu überprüfen und Informationen zu vergleichen, um sicherzustellen, dass ich meinen Lesern eine klare und fundierte Sichtweise präsentiere. Mein Ziel ist es, Wissen so zu organisieren, dass es leicht verständlich ist und dabei hilft, die Herausforderungen der digitalen Welt zu meistern.

Kommentar schreiben