Zum Hauptinhalt springen

UNABHÄNGIGES WISSENSFORUM Unabhängige Hosting-Community • Keine offizielle Verbindung zur netcup GmbH.

NETCUP GUIDE
FORUM · GUTSCHEINPOOL
Tutorials Stufe: Mittelstufe Dauer: 20 Min. // 18 Min. Lesezeit • Geprüft:

Nginx als hochperformanten HTTP/2- & HTTP/3-Reverse-Proxy auf Netcup konfigurieren

Ein optimal abgestimmter Nginx-Edge-Reverse-Proxy vor Ihren Docker-Containern, Node.js/Go-APIs oder Python-Gunicorn-Workern auf einem Netcup-Server ermöglicht es Ihnen, TLS 1.3 und HTTP/3 (QUIC) zentral zu terminieren, persistente Upstream-Keepalive-Verbindungen zu bündeln, IP-basiertes Rate-Limiting gegen Credential-Stuffing durchzusetzen und statische Assets direkt aus dem Linux-Kernel-Page-Cache (sendfile on) auszuliefern. Dieser Leitfaden liefert eine produktionsreife Nginx-Konfiguration für Multi-Core AMD EPYC Netcup-Nodes.

THREAD-AUTOR // OP Elena Rostova Lead Cloud Infrastructure Architect (EU KVM & Netzwerk-Engineering)
MOD-PRÜFUNG // CO-MAINTAINER Marcus Vance Senior Linux Systems & Security Auditor
BEITRAGSSTATUS // AKTUALISIERT Community-Standards →
MODERATOR-HINWEIS // TESTUMGEBUNG & TRANSPARENZ Verifiziert mit Nginx 1.26+ (Ubuntu 24.04 & offizielles nginx.org Mainline-Paket) inklusive OpenSSL 3.0 TLS 1.3, HTTP/3 (ngx_http_v3_module) und Certbot systemd-Timern.

VORAUSSETZUNGEN // ANFORDERUNGEN

CHECKLISTE
  • ✓ Ein Netcup VPS (z. B. VPS 500 G12.5 mit 4 vCPU oder VPS 1000 G12.5 mit 6 vCPU) oder Root-Server (RS 1000 G12.5 mit 4 dedizierten EPYC-Kernen) unter Ubuntu 24.04 LTS oder Debian 12 mit geöffneten Ports 80/tcp, 443/tcp sowie 443/udp
  • ✓ Ein Domainname mit DNS A- (IPv4) und AAAA- (IPv6) Records, die auf Ihren Netcup-Server zeigen

Wichtige Erkenntnisse (Key Takeaways)

KURZFAZIT
  • 01 Optimieren Sie worker_processes auto; und worker_rlimit_nofile 65535; in der /etc/nginx/nginx.conf, damit Nginx über alle dedizierten oder geteilten AMD EPYC-Kerne skaliert, ohne an das Standardlimit von 1.024 Datei-Deskriptoren zu stoßen.
  • 02 Nutzen Sie einen upstream-Block mit keepalive 32; und einer WebSocket-Upgrade-Map, um die Erschöpfung ephemerer TCP-Ports (TIME_WAIT) zwischen Nginx und lokalen 127.0.0.1-Backend-Containern zu verhindern.
  • 03 Konfigurieren Sie eine Shared-Memory-Rate-Limiting-Zone (limit_req_zone $binary_remote_addr), um /login- und /api/-Endpunkte vor Layer-7-Floods zu schützen.
📑 Inhaltsverzeichnis (3 Abschnitte) ▼
§ 01

Schritt 1: Nginx & Certbot installieren und globale Worker-Limits (/etc/nginx/nginx.conf) optimieren

Ubuntu 24.04 LTS liefert Nginx 1.24+ ab Werk aus; alternativ können Sie das offizielle nginx.org-Mainline-Repository für Nginx 1.26/1.27 mit vollem HTTP/3-QUIC-Support nutzen. Nach der Installation von nginx und certbot besteht der erste Schritt darin, die Ereignis-Architektur (epoll) und die Worker-Prozesse an die CPU-Topologie Ihres Netcup-Servers anzupassen. Mit worker_processes auto; startet Nginx exakt einen ereignisgesteuerten Worker-Prozess pro verfügbarem vCPU- bzw. dediziertem AMD EPYC Zen 5 Kern (z. B. 6 Worker auf einem VPS 1000 G12.5 oder 4 dedizierte Worker auf einem RS 1000 G12.5), sodass jeder Kern Tausende gleichzeitiger Verbindungen ohne Thread-Locking-Overhead abarbeiten kann.

Ein kritischer Flaschenhals in fast jeder Standardinstallation ist das Linux-Limit für geöffnete Datei-Deskriptoren (RLIMIT_NOFILE, standardmäßig oft nur 1024). Da unter Linux jeder TCP-Socket ein Datei-Deskriptor ist, verbraucht jede einzelne über einen Reverse-Proxy geleitete Besucheranfrage zwei Datei-Deskriptoren gleichzeitig: einen für den eingehenden TLS-Socket des Besuchers auf Port 443 und einen zweiten für den lokalen Loopback-Socket (127.0.0.1:3000) zum Backend-Container. Ohne das Setzen von worker_rlimit_nofile 65535; in der /etc/nginx/nginx.conf wirft Nginx bereits bei rund 500 parallelen Besuchern den Fehler socket() failed (24: Too many open files) im error.log.

Für den Schutz von Login-, Registrierungs- und API-Routen vor automatisierten Brute-Force- und Layer-7-Flood-Angriffen richten wir eine Leaky-Bucket-Rate-Limiting-Zone über limit_req_zone $binary_remote_addr zone=api_limit:10m rate=15r/s; ein. Die Variable $binary_remote_addr benötigt für eine IPv4-Adresse lediglich 4 Byte (bzw. 16 Byte für IPv6) statt bis zu 15 Byte als ASCII-Text ($remote_addr). Dadurch passen in einen nur 10 MB großen Shared-Memory-Bereich rund 160.000 gleichzeitige Besucher-IP-Zustände, ohne den Arbeitsspeicher Ihres Servers zu belasten.

Zusätzlich definieren wir in /etc/nginx/conf.d/00-netcup-tuning.conf eine globale map $http_upgrade $connection_upgrade-Anweisung. Warum darf man in einem Reverse-Proxy nicht einfach pauschal proxy_set_header Connection "upgrade"; hartcodieren? Weil ein fest gesetzter upgrade-Header das HTTP/1.1-Keepalive-Pooling für sämtliche normalen REST- und HTML-Anfragen außer Kraft setzen würde! Über die map-Direktive sendet Nginx Connection: upgrade ausschließlich dann, wenn der Client tatsächlich einen WebSocket-Handshake (Upgrade: websocket) anfordert, und übergibt bei allen regulären HTTP-Anfragen einen leeren String (""), damit die TCP-Verbindung im Upstream-Keepalive-Pool offen bleibt.

Empfohlenes Nginx Worker- & Upstream-Tuning nach Netcup Server-Hardware
Netcup Server-ModellCPU- & RAM-Zuweisungworker_processes & worker_connectionsworker_rlimit_nofile LimitUpstream keepalive Pool-Größe
VPS nano G11.5s2 vCPU-Kerne / 2 GB ECC-RAMauto (2 Worker) × 4.096 Verbindungen32.768 Datei-Deskriptorenkeepalive 16;
VPS 500 G12.54 vCPU-Kerne / 4 GB ECC-RAMauto (4 Worker) × 4.096 Verbindungen65.535 Datei-Deskriptorenkeepalive 32;
VPS 1000 G12.56 vCPU-Kerne / 8 GB ECC-RAMauto (6 Worker) × 8.192 Verbindungen65.535 Datei-Deskriptorenkeepalive 64;
RS 1000 G12.54 Dedizierte AMD EPYC Zen 5 Kerne / 8 GB DDR5auto (4 Worker) × 16.384 Verbindungen65.535 Datei-Deskriptorenkeepalive 64;
Nginx + Certbot installieren & kompilierte Module prüfen
bash
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx

# Nginx-Version sowie einkompilierte Module prüfen (TLS 1.3, HTTP/2, HTTP/3, RealIP)
nginx -V 2>&1 | tr ' ' '\n' | grep -E "http_v2|http_v3|realip|ssl"

Befehlserklärung: Installiert Nginx und das Certbot-Nginx-Plugin und prüft anschließend die einkompilierten Module auf HTTP/2-, HTTP/3- und RealIP-Unterstützung.

ERWARTETE AUSGABE
--with-http_ssl_module
--with-http_v2_module
--with-http_v3_module
--with-http_realip_module
/etc/nginx/conf.d/00-netcup-tuning.conf (Rate-Limiting, WebSocket-Map & Limits)
nginx
# Nginx-Versionsnummer in HTTP-Antwortheadern und Fehlerseiten verbergen
server_tokens off;

# Dynamische Connection-Upgrade-Map für nahtloses WebSocket-Proxying (wss://)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      '';
}

# IP-basierte Rate-Limiting-Zone: 10 MB Shared Memory (~160.000 IP-Zustände), 15 Req/s Basisrate
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=15r/s;
limit_req_status 429;

# Maximale Upload-Größe & Keepalive-Timeouts
client_max_body_size 64M;
keepalive_timeout 65;
types_hash_max_size 4096;

Befehlserklärung: Hinterlegt globale Reverse-Proxy-Maps und eine 10 MB große Shared-Memory-Rate-Limiting-Zone in /etc/nginx/conf.d/00-netcup-tuning.conf, sodass jeder virtuelle Host auf $connection_upgrade und zone=api_limit zugreifen kann.

§ 02

Schritt 2: Produktionsreifen Upstream- & TLS 1.3-Reverse-Proxy-Serverblock erstellen

Viele Anleitungen im Netz zeigen lediglich ein direktes proxy_pass http://127.0.0.1:3000; ohne eigenen upstream-Block. Standardmäßig nutzt Nginx für Backend-Verbindungen jedoch HTTP/1.0 mit Connection: close: Für jede einzelne Anfrage muss der Kernel einen neuen dreistufigen TCP-Handshake (SYN, SYN-ACK, ACK) über das Loopback-Interface ausführen und den Socket anschließend für 60 Sekunden in den Zustand TIME_WAIT versetzen. Bei Lastspitzen sind die rund 28.000 ephemeren Quellports (net.ipv4.ip_local_port_range) binnen Sekunden erschöpft, und Nginx quittiert neue Anfragen mit 502 Bad Gateway (Cannot assign requested address).

Durch die Definition eines upstream app_backend-Blocks mit keepalive 32; in Kombination mit proxy_http_version 1.1; hält jeder Nginx-Worker-Prozess bis zu 32 warme, bereits geöffnete TCP-Sockets zu Ihrem Docker-Container oder Node.js/Go-Prozess im Pool bereit. Eingehende HTTP/2- und HTTP/3-Anfragen werden ohne erneuten TCP-Handshake sofort über diese persistenten Verbindungen durchgereicht, was die Antwortlatenz und die CPU-Interrupt-Last auf Ihrem Netcup-Server drastisch senkt.

Auf kryptografischer Ebene aktivieren wir TLS 1.2 und TLS 1.3 mit AEAD-Chiffren (ECDHE-ECDSA-AES128-GCM-SHA256, CHACHA20-POLY1305) und deaktivieren Session-Tickets (ssl_session_tickets off), um die Forward Secrecy zu wahren. Ein wichtiger Sicherheitshinweis zu TLS 1.3 0-RTT (ssl_early_data): Auch wenn 0-RTT beim Wiederverbinden einen Round-Trip einspart, sind 0-RTT-Pakete auf Protokollebene anfällig für Replay-Angriffe. Falls ein Angreifer ein verschlüsseltes 0-RTT-Paket mitschneidet und erneut an Ihren Server sendet, könnte eine nicht-idempotente Anfrage doppelt ausgeführt werden. Für API- und Applikations-Backends lassen wir ssl_early_data daher bewusst ausgeschaltet.

Innerhalb des location /-Blocks setzen wir limit_req zone=api_limit burst=30 nodelay; ein: Moderne Webanwendungen laden beim ersten Seitenaufruf oft 20 bis 25 Assets, Chunks und API-Endpunkte gleichzeitig. Ohne burst=30 nodelay würde eine strikte Rate von 15r/s bereits ab dem zweiten Paket innerhalb derselben Zehntelsekunde legitime Besucher mit 429 Too Many Requests blockieren. Der burst=30-Puffer erlaubt kurze, natürliche Ladestöße sofort (nodelay), drosselt aber anhaltendes automatisiertes Scraping zuverlässig auf 15 Anfragen pro Sekunde. Gleichzeitig vergrößern wir die Header-Puffer (proxy_buffer_size 32k;), damit große OAuth2/JWT-Session-Cookies oder Content-Security-Policy-Header von Next.js, Laravel oder Keycloak niemals zu 502 upstream sent too big header-Fehlern führen:

/etc/nginx/sites-available/app.example.com.conf
nginx
upstream app_backend {
    server 127.0.0.1:3000 max_fails=3 fail_timeout=10s;
    keepalive 32;
}

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

    # ACME HTTP-01 Challenge für Certbot durchlassen, alles andere auf HTTPS umleiten
    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name app.example.com;

    # TLS-Zertifikate (werden in Schritt 3 durch Certbot verwaltet)
    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    # Moderne Mozilla TLS 1.2 / 1.3 Cipher-Konfiguration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # Gehärtete HTTP-Security-Header
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Primärer Applikations- & WebSocket-Reverse-Proxy
    location / {
        limit_req zone=api_limit burst=30 nodelay;

        proxy_pass http://app_backend;
        proxy_http_version 1.1;

        # Unterstützt sowohl Keepalive-Pooling als auch WebSocket-Upgrades
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Ursprüngliche Client-Metadaten an das Backend übergeben
        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;
        proxy_set_header X-Forwarded-Host $host;

        # Proxy-Buffer- & Timeout-Tuning
        proxy_buffering on;
        proxy_buffers 16 16k;
        proxy_buffer_size 32k;
        proxy_connect_timeout 10s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Befehlserklärung: Konfiguriert Dual-Stack IPv4/IPv6-Listener (listen 443 ssl; listen [::]:443 ssl;), moderne http2 on;-Syntax, TLS 1.3 Ciphers, HSTS-Header, Rate-Limiting mit Burst-Puffer (burst=30 nodelay) sowie WebSocket-fähiges Upstream-Connection-Pooling.

Moderne http2 on; Direktive ab Nginx 1.25+ verwenden

Die veraltete Syntax listen 443 ssl http2; erzeugt ab Nginx 1.25.1 Warnmeldungen. Nutzen Sie immer listen 443 ssl; und listen [::]:443 ssl; gefolgt von einer separaten Zeile http2 on;.

§ 03

Schritt 3: Let’s Encrypt TLS-Zertifikate ausstellen & unterbrechungsfreie Reloads prüfen

Bevor Sie den vollständigen HTTPS-Serverblock scharfschalten, rufen Sie über certbot --nginx das initiale Let’s Encrypt Zertifikat ab. Achten Sie bei Dual-Stack-Servern auf Netcup besonders darauf, dass Ihre Domain im DNS nicht nur auf Ihre IPv4-Adresse (A-Record), sondern auch auf die korrekte IPv6-Adresse Ihres /64-Subnetzes (AAAA-Record) zeigt und in jedem server-Block sowohl listen 80; als auch listen [::]:80; eingetragen ist. Da die Validierungsserver von Let’s Encrypt bei Vorhandensein eines AAAA-Eintrags bevorzugt über IPv6 verbinden, führt ein fehlender IPv6-Listener in Nginx sofort zu einem ACME-HTTP-01-Timeout.

Unter Ubuntu 24.04 LTS und Debian 12 richtet das Paket certbot automatisch den systemd-Timer certbot.timer ein, der zweimal täglich zu zufällig verteilten Zeiten (RandomizedDelaySec) prüft, ob ein Zertifikat innerhalb der nächsten 30 Tage abläuft. Sobald eine Verlängerung ansteht, erneuert Certbot das Schlüsselpaar und lädt die Nginx-Worker nahtlos über den Deploy-Hook neu.

Verwenden Sie im laufenden Produktionsbetrieb bei Konfigurations- oder Zertifikatsänderungen grundsätzlich sudo nginx -t && sudo systemctl reload nginx anstelle von systemctl restart nginx. Ein reload sendet das POSIX-Signal SIGHUP an den Nginx-Master-Prozess: Dieser validiert zunächst die Konfigurationsdatei, liest die neuen TLS-Zertifikate in den Speicher und startet frische Worker-Prozesse, während die alten Worker noch laufende Datei-Downloads oder API-Requests sauber zu Ende bedienen (Graceful Shutdown), bevor sie sich beenden.

Wenn Sie auf Nginx 1.25+ zusätzlich HTTP/3 (QUIC) aktivieren möchten, ergänzen Sie im HTTPS-Block listen 443 quic reuseport; sowie listen [::]:443 quic reuseport; (beachten Sie, dass der Kernel-Parameter reuseport nur in genau einem server-Block pro IP/Port-Paar stehen darf, meist im default_server) und senden den Antwortheader add_header Alt-Svc 'h3=":443"; ma=86400' always;. Über diesen Alt-Svc-Header erfahren moderne Browser beim ersten TLS-1.3-Aufruf über TCP, dass Ihr Netcup-Server auf UDP-Port 443 QUIC spricht, und wickeln Folgeverbindungen multiplexiert und ohne TCP-Head-of-Line-Blocking über UDP ab:

Let’s Encrypt Zertifikat abrufen & automatischen Systemd-Renewal-Timer prüfen
bash
# 1. Zertifikat über Certbot ausstellen
sudo certbot --nginx -d app.example.com --non-interactive --agree-tos -m admin@example.com --redirect

# 2. Symlink aktivieren, Nginx-Konfigurationssyntax testen und Worker ohne Unterbrechung neu laden
sudo ln -sf /etc/nginx/sites-available/app.example.com.conf /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

# 3. Den zweimal täglich laufenden systemd-Renewal-Timer von Certbot prüfen und per Dry-Run testen
sudo systemctl status certbot.timer --no-pager
sudo certbot renew --dry-run

Befehlserklärung: Stellt das Let’s Encrypt Zertifikat aus, entfernt die Standard-Platzhalterseite, prüft die Syntax via nginx -t und verifiziert die automatische Verlängerung über certbot.timer.

ERWARTETE AUSGABE
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Congratulations, all simulated renewals succeeded

Häufige Fehlerbehebung

DIAGNOSE-GUIDE

[SYMPTOM] Nginx liefert sofort nach dem Aktivieren der Seite 502 Bad Gateway

Ursache: Ihr Upstream-Container (127.0.0.1:3000) läuft nicht, lauscht auf einem anderen Port oder ist beim Start abgestürzt.

Lösung: Prüfen Sie mit sudo ss -tulpn | grep 3000, ob der Backend-Prozess an 127.0.0.1:3000 gebunden ist, und kontrollieren Sie /var/log/nginx/error.log (connect() failed (111: Connection refused)).

[SYMPTOM] WebSocket-Verbindungen (wss://) brechen exakt alle 60 Sekunden ab

Ursache: Das Standardlimit proxy_read_timeout von Nginx beträgt 60 Sekunden. Sendet Ihr WebSocket-Server innerhalb von 60 Sekunden keine Ping/Pong-Frames, schließt Nginx die Verbindung.

Lösung: Aktivieren Sie in Ihrer Anwendung WebSocket-Heartbeats alle 30 Sekunden oder erhöhen Sie proxy_read_timeout 3600s; im entsprechenden location-Block.

[SYMPTOM] Certbot ACME HTTP-01 Challenge schlägt über IPv6 fehl (Timeout during connect)

Ursache: Für Ihre Domain existiert ein AAAA-DNS-Record (Let’s Encrypt bevorzugt IPv6), aber in Ihrem Nginx-Block fehlt listen [::]:80; oder Ihr Netcup-IPv6-Gateway (fe80::1) ist fehlerhaft.

Lösung: Stellen Sie sicher, dass jeder server-Block sowohl listen 80; als auch listen [::]:80; enthält, und testen Sie IPv6 mit ping6 -c 3 2606:4700:4700::1111.

Häufig gestellte Fragen (FAQ)

GEPRÜFTE ANTWORTEN

Warum sollte ich systemctl reload nginx statt systemctl restart nginx verwenden?

systemctl reload nginx sendet ein SIGHUP-Signal an den Master-Prozess: Dieser prüft zuerst die Syntax der /etc/nginx/nginx.conf, startet neue Worker-Prozesse mit der aktualisierten Konfiguration und lässt alte Worker bestehende Anfragen sauber zu Ende bearbeiten – ganz ohne abgebrochene Verbindungen.

Kann Nginx mehrere Docker-Anwendungen auf demselben Netcup-Server als Reverse-Proxy bedienen?

Ja! Das ist der Hauptvorteil eines Host-Nginx-Reverse-Proxys. Binden Sie App A an 127.0.0.1:3001, App B an 127.0.0.1:3002 und App C an 127.0.0.1:3003 und erstellen Sie drei separate /etc/nginx/sites-available/*.conf-Dateien.

Wie aktiviere ich HTTP/3 (QUIC) ab Nginx 1.25+ auf Netcup?

Ergänzen Sie listen 443 quic reuseport; und listen [::]:443 quic reuseport; im primären server-Block (reuseport darf nur einmal pro IP/Port-Paar angegeben werden), fügen Sie add_header Alt-Svc 'h3=":443"; ma=86400' always; hinzu und geben Sie 443/udp in UFW sowie der Netcup SCP Firewall frei.

Warum meldet Nginx upstream sent too big header while reading response header from upstream?

Anwendungen, die große JWT-Cookies oder umfangreiche CSP-Header setzen (z. B. Next.js, Keycloak oder Laravel), überschreiten schnell den 4-KB-Standardpuffer von Nginx. Mit proxy_buffer_size 32k; und proxy_buffers 16 16k; (wie in Schritt 2 konfiguriert) ist das Problem dauerhaft behoben.

QUELLEN // GEPRÜFTE TECHNISCHE DOKUMENTATION

WEITERFÜHREND // EMPFOHLENE ARCHITEKTUR-AUDITS

Gutscheine →