Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Linux 02.06.2026 · 9 min Lesezeit

Nginx als Reverse Proxy mit TLS manuell einrichten

So richtest du <strong>Nginx als Reverse Proxy mit TLS</strong> auf Debian 12 oder Ubuntu 24.04 von Hand ein: proxy_pass auf ein Backend, die Pflicht-Proxy-Header, WebSocket-Upgrade und ein Let’s-Encrypt-Zertifikat per certbot – copy-paste-fertig.

Infografik, die Nginx als Reverse Proxy mit TLS zeigt, mit HTTPS-Zugriff auf Port 443, Zertifikat, privatem Schlüssel, Weiterleitung an Backend-Server und gesetzten Proxy-Headern.

Ein Nginx Reverse Proxy mit TLS nimmt Anfragen aus dem Internet auf Port 443 entgegen, terminiert das HTTPS-Zertifikat und reicht die Requests intern an dein Backend weiter – etwa eine Node-, Python- oder PHP-App, die auf 127.0.0.1:8080 lauscht. Diese Anleitung richtet das auf Debian 12 oder Ubuntu 24.04 komplett von Hand ein: Server-Block mit proxy_pass, die vier Pflicht-Header, WebSocket-Upgrade und ein Let’s-Encrypt-Zertifikat per certbot --nginx.

Bewusst ohne GUI-Werkzeuge: Wir nutzen weder den Nginx Proxy Manager noch Caddy mit Auto-HTTPS, sondern schreiben die Konfig selbst. So verstehst du jede Zeile und kannst gezielt debuggen, wenn etwas klemmt.

Kurzfassung:

  1. apt install nginx, Server-Block in /etc/nginx/sites-available/ anlegen und per Symlink aktivieren.
  2. Kern: proxy_pass http://127.0.0.1:8080; plus die vier Header Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto.
  3. WebSockets: map $http_upgrade-Block, proxy_http_version 1.1; und die Header Upgrade/Connection.
  4. TLS: certbot --nginx -d DEINE-DOMAIN holt das Zertifikat und legt den HTTP→HTTPS-Redirect an.
  5. Nach jeder Änderung: nginx -t testen, dann systemctl reload nginx.

Voraussetzungen

  1. Ein Server mit Debian 12 oder Ubuntu 24.04 LTS und sudo-Rechten (Nginx 1.22.x bzw. 1.24.x aus den Paketquellen).
  2. Eine Domain, deren A-/AAAA-Record bereits auf die öffentliche IP des Servers zeigt – ohne korrektes DNS kann Let’s Encrypt die Domain nicht validieren.
  3. Ein erreichbares Backend, z. B. eine App auf http://127.0.0.1:8080.
  4. Die Ports 80 und 443 müssen von außen offen sein. Port 80 bleibt für die ACME-Validierung Pflicht – auch nach dem Redirect.

Schritt 1: Nginx installieren und starten

Installiere das Paket aus den Distributionsquellen und aktiviere den Dienst dauerhaft, damit er auch nach einem Reboot wieder startet.

sudo apt update && sudo apt install -y nginx

# Dienst starten und beim Boot aktivieren
sudo systemctl enable --now nginx

# Status prüfen
systemctl status nginx --no-pager

Falls auf dem Server eine UFW-Firewall läuft, gib direkt beide Ports frei. Das Profil Nginx Full öffnet 80 und 443:

sudo ufw allow 'Nginx Full'
sudo ufw status

Ein Aufruf von http://DEINE-DOMAIN zeigt jetzt die Nginx-Standardseite. Damit steht die Basis.

Schritt 2: Server-Block als Reverse Proxy anlegen

Debian und Ubuntu trennen Konfig-Dateien in /etc/nginx/sites-available/ (vorrätig) und /etc/nginx/sites-enabled/ (aktiv, per Symlink). Lege eine Datei pro Domain an. Wir starten bewusst mit reinem HTTP auf Port 80 – das TLS ergänzt certbot später automatisch.

sudo nano /etc/nginx/sites-available/example.com

Inhalt der Datei – ersetze example.com durch deine Domain und 8080 durch den Port deines Backends:

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

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

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

Warum diese vier Header? Nginx verändert per Default nur Host (auf $proxy_host) und Connection (auf close). Alles andere musst du selbst setzen:

  1. Host – damit dein Backend den ursprünglich angefragten Hostnamen sieht (wichtig für virtuelle Hosts und korrekte Links).
  2. X-Real-IP – die echte Client-IP; ohne diesen Header sieht das Backend nur 127.0.0.1.
  3. X-Forwarded-For$proxy_add_x_forwarded_for hängt die Client-IP an eine eventuell vorhandene Proxy-Kette an.
  4. X-Forwarded-Proto – sagt dem Backend, dass extern HTTPS gesprochen wird. Fehlt das, baut die App falsche absolute URLs oder gerät in Redirect-Schleifen.

Aktiviere den Block per Symlink, teste die Syntax und lade neu:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

# Optional: Default-Seite deaktivieren, damit sie nicht stört
sudo rm -f /etc/nginx/sites-enabled/default

sudo nginx -t
sudo systemctl reload nginx

Schritt 3: Trailing-Slash bei proxy_pass verstehen

Der häufigste Stolperstein beim Reverse Proxy ist der abschließende Schrägstrich hinter proxy_pass. Er entscheidet, welchen Pfad das Backend bekommt:

DirektiveVerhalten

proxy_pass http://127.0.0.1:8080;

ohne Slash – der angefragte Pfad wird unverändert weitergereicht.

proxy_pass http://127.0.0.1:8080/;

mit Slash – der location-Präfix wird ersetzt (abgeschnitten).

Beispiel: Bei location /api/ und einem Request auf /api/users sendet die Variante mit Slash /users ans Backend, die Variante ohne Slash dagegen /api/users. Setzt du das falsch, antwortet das Backend mit 404 oder 502. Im Zweifel: erst ohne Slash testen, dann gezielt anpassen.

Schritt 4: WebSocket-Upgrade aktivieren

WebSocket-Verbindungen (Chat, Live-Dashboards, Hot-Reload) brauchen die Header Upgrade und Connection. Das sind Hop-by-Hop-Header – Nginx reicht sie nicht automatisch weiter. Definiere zuerst auf http-Ebene einen map-Block. Lege ihn in eine eigene Datei unter /etc/nginx/conf.d/, dann gilt er global:

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

Erweitere anschließend den location-Block in deinem Server-Block um drei Zeilen:

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

    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;

    # WebSocket-Upgrade
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
}

Wichtig zum proxy_http_version 1.1;: Seit Nginx 1.29.7 (März 2026) ist HTTP/1.1 zum Upstream der Default und Keepalive ist automatisch aktiv. Auf den älteren Versionen aus Debian 12 (1.22.x) und Ubuntu 24.04 (1.24.x) musst du die Zeile aber weiterhin manuell setzen, sonst brechen WebSockets ab. Sterben lange Idle-Verbindungen am 60-Sekunden-Default, erhöhe zusätzlich proxy_read_timeout:

    proxy_read_timeout 3600s;

Danach wie immer testen und neu laden:

sudo nginx -t && sudo systemctl reload nginx

Schritt 5: TLS-Zertifikat per certbot holen

Jetzt kommt das HTTPS. Wir nutzen Let’s Encrypt über certbot mit dem --nginx-Plugin: Es modifiziert deine Server-Block-Datei automatisch, trägt listen 443 ssl; samt Zertifikatspfaden ein und richtet die Erneuerung ein. Installiere certbot – entweder klassisch per apt:

sudo apt install -y certbot python3-certbot-nginx

Oder, wenn du die jeweils neueste Version willst (so empfiehlt es die EFF offiziell), per snap. Entferne dann vorher das apt-Paket, sonst läuft womöglich die falsche Version:

sudo apt-get remove certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot

Hole nun das Zertifikat. certbot fragt nach deiner E-Mail-Adresse und – wichtig – ob HTTP automatisch auf HTTPS umgeleitet werden soll. Wähle dort die Redirect-Option:

sudo certbot --nginx -d example.com -d www.example.com

certbot legt die Zertifikate unter /etc/letsencrypt/live/example.com/ ab (fullchain.pem und privkey.pem) und schreibt einen zweiten Server-Block für Port 443 in deine Konfig. Deine Seite ist jetzt unter https://DEINE-DOMAIN erreichbar; HTTP wird auf HTTPS umgeleitet. Zertifikatsdetails (Challenge-Typen, Wildcard, DNS-01) findest du im verlinkten certbot-Artikel am Ende.

Schritt 6: Automatische Erneuerung prüfen

certbot installiert einen systemd-Timer (certbot.timer), der zweimal täglich läuft und Zertifikate erneuert, die binnen 30 Tagen ablaufen. Prüfe, dass der Timer aktiv ist, und teste die Erneuerung gefahrlos im Trockenlauf:

# Timer-Status anzeigen
systemctl list-timers | grep certbot
systemctl status certbot.timer --no-pager

# Erneuerung simulieren (nutzt Let's-Encrypt-Staging, kein echtes Zertifikat)
sudo certbot renew --dry-run

Meldet der Trockenlauf Congratulations, all simulated renewals succeeded, ist alles bereit. Soll Nginx nach jeder Erneuerung automatisch neu laden, lege ein Deploy-Hook-Skript an:

echo '#!/bin/sh
systemctl reload nginx' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Schritt 7: Mehrere Backends über Pfade routen

Oft soll ein Server mehrere Dienste bündeln: eine API auf einem Port, ein Chat-Backend auf einem anderen. Das löst du über mehrere location-Blöcke im selben Server-Block. Achte hier besonders auf den Trailing-Slash aus Schritt 3:

server {
    listen 443 ssl;
    server_name example.com;

    # ... ssl_certificate-Zeilen von certbot ...

    location /api/ {
        proxy_pass http://127.0.0.1:8080/;
        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 /chat/ {
        proxy_pass http://127.0.0.1:9001/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host       $host;
    }
}

Brauchst du stattdessen getrennte Domains, lege je Domain eine eigene Datei in sites-available/ mit eigenem server_name an und hole für jede ein eigenes Zertifikat. Nginx wählt anhand des server_name den passenden Block.

Schritt 8: Konfig testen und sicher aktivieren

Diese zwei Befehle gehören nach jeder Änderung zusammen. Erst der Syntax-Check, dann das unterbrechungsfreie Neuladen:

sudo nginx -t
# erwartete Ausgabe: syntax is ok / test is successful

sudo systemctl reload nginx

Nutze reload statt restart: Ein Reload lädt die Konfig graceful, ohne aktive Verbindungen zu trennen. Ein restart kappt alle Verbindungen und ist nur nötig, wenn du Ports oder Interfaces in der listen-Zeile änderst. Überspringe niemals nginx -t – eine kaputte Konfig wird beim Reload nicht übernommen, und ein Restart mit Fehler legt den Dienst lahm.

Troubleshooting

502 Bad Gateway: Das Backend ist nicht erreichbar. Prüfe, ob der Dienst läuft, ob Port und Host stimmen und ob das Backend wirklich auf 127.0.0.1 (oder dem konfigurierten Interface) lauscht – nicht versehentlich nur auf einem anderen Interface. Schnelltest:

curl -v http://127.0.0.1:8080
sudo ss -tlnp | grep 8080
sudo tail -f /var/log/nginx/error.log

Falsche Pfade (404 vom Backend): Meist der Trailing-Slash bei proxy_pass (siehe Schritt 3). Vergleiche im Backend-Log, welcher Pfad ankommt.

Redirect-Schleifen oder falsche Links: Es fehlt X-Forwarded-Proto $scheme;. Das Backend hält die Verbindung dann für HTTP und leitet endlos auf HTTPS um. Header ergänzen, reload.

WebSocket bricht ab: map-Block fehlt, die Upgrade/Connection-Header sind nicht gesetzt oder proxy_http_version 1.1; fehlt auf älterem Nginx.

certbot schlägt fehl: DNS zeigt nicht auf den Server, oder Port 80 ist blockiert. Beides ist für die ACME-HTTP-01-Validierung Pflicht – auch nach aktiviertem Redirect.

Häufige Fragen

Warum nicht einfach den Nginx Proxy Manager oder Caddy nehmen?

Beide sind komfortabel, nehmen dir aber die Kontrolle aus der Hand. Wer Nginx manuell konfiguriert, kann jede Direktive gezielt setzen, sie versionieren und im Fehlerfall präzise debuggen. Diese Anleitung bleibt deshalb bewusst beim handgeschriebenen Nginx.

Muss ich proxy_http_version 1.1 wirklich setzen?

Auf Debian 12 (Nginx 1.22.x) und Ubuntu 24.04 (1.24.x): ja, sonst funktionieren WebSockets und Upstream-Keepalive nicht. Erst ab Nginx 1.29.7 ist HTTP/1.1 zum Upstream der Default und die Zeile wäre überflüssig.

Wie schütze ich mich vor gefälschten X-Forwarded-For-Headern?

Vertraue dem Header nur von bekannten Proxys. Nutze das real_ip-Modul mit set_real_ip_from für deine Proxy-IPs und reiche X-Forwarded-For nicht ungefiltert durch, wenn der Proxy direkt am Internet hängt – sonst kann ein Client seine IP spoofen.

Reload oder Restart nach Änderungen?

In aller Regel reload – das ist graceful und trennt keine Verbindungen. restart brauchst du nur, wenn du die listen-Direktive (Port/Interface) änderst.

Wo liegen die Zertifikate und wie oft werden sie erneuert?

Unter /etc/letsencrypt/live/DEINE-DOMAIN/. Der certbot.timer läuft zweimal täglich und erneuert automatisch, sobald weniger als 30 Tage Restlaufzeit bleiben.

Fazit

Ein Nginx Reverse Proxy mit TLS ist in wenigen Schritten von Hand aufgesetzt: Paket installieren, Server-Block mit proxy_pass und den vier Pflicht-Headern schreiben, bei Bedarf WebSockets per map-Block freischalten und das Zertifikat mit certbot --nginx holen. Mit der Disziplin, vor jedem systemctl reload nginx ein nginx -t laufen zu lassen, bleibt der Betrieb stabil und unterbrechungsfrei. Achte besonders auf den Trailing-Slash bei proxy_pass und auf X-Forwarded-Proto – das sind die zwei häufigsten Fehlerquellen im Mittelstands-Alltag.

Weiterführende Anleitungen und Quellen

  1. Let’s Encrypt mit certbot: TLS-Zertifikate im Detail – Challenge-Typen, Wildcard- und DNS-01-Validierung sowie Renewal-Feinheiten.
  2. Docker Compose Grundlagen und Stacks – wenn dein Backend in einem Container statt direkt auf dem Host läuft.
  3. systemd-Service erstellen und verwalten – damit dein Backend zuverlässig als Dienst startet, bevor Nginx daraufzeigt.
  4. Weitere Linux-Anleitungen in der Kategorie Linux

Quellen:

  1. nginx.org – ngx_http_proxy_module (proxy_pass, proxy_set_header)
  2. nginx.org – WebSocket proxying (map $http_upgrade)
  3. Certbot EFF – offizielle Nginx-Installationsanleitung
  4. NGINX Blog – Keep-alive to upstreams default in 1.29.7