Nginx ist ein schneller Webserver. Wir nutzen ihn, um eine statische Website auszuliefern oder ihn als Reverse Proxy vor Ihre App zu setzen, und schalten HTTPS mit einem Let's-Encrypt-Zertifikat ein, das sich selbst erneuert.
Was Sie brauchen
- Ein StormHost-VPS mit Ubuntu 24.04 oder Debian 13
- Eine auf die IP-Adresse des VPS zeigende Domain (ein
A-Eintrag, optionalAAAA); siehe Domain auf einem VPS und Cloudflare - Eine SSH-Verbindung als root (Anleitung)
- Etwa 25 Minuten
So funktioniert es#
Der Browser verbindet sich mit Port 80 (HTTP) oder 443 (HTTPS) Ihres VPS. Anhand der Domain in der Anfrage wählt Nginx den passenden Server-Block (die Konfiguration einer Website) und sendet entweder eine Datei von der Festplatte (statische Website) oder reicht die Anfrage an eine App weiter, die auf einem anderen Port läuft (Reverse Proxy).
Das HTTPS-Zertifikat stellt Let's Encrypt kostenlos aus. Das Programm Certbot prüft, dass Sie die Domain kontrollieren (Let's Encrypt verbindet sich mit Port 80 und lädt eine Prüfdatei herunter), lädt das Zertifikat herunter, richtet es in Nginx ein und erneuert es danach von selbst.
Voraussetzungen: DNS und Ports#
Prüfen Sie, dass die Domain auf den VPS zeigt. Führen Sie auf dem VPS aus:
bash getent hosts example.czSie sehen die IP-Adresse Ihres VPS (aus der Registerkarte Netzwerk). Falls nicht, korrigieren Sie das DNS und warten Sie, bis sich die Änderung verbreitet hat.
Öffnen Sie die Ports 80 und 443:
- Firewall im Kundenbereich (wenn Sie den Modus Alles blockieren außer Erlaubtem verwenden): Regel Eingehend · Erlauben · TCP · Port
80,443. Anleitung: Firewall im Kundenbereich. - UFW auf dem VPS (wenn Sie es verwenden):
ufw allow 80/tcpundufw allow 443/tcp.
- Firewall im Kundenbereich (wenn Sie den Modus Alles blockieren außer Erlaubtem verwenden): Regel Eingehend · Erlauben · TCP · Port
Cloudflare: Zertifikat mit grauer Wolke holen
Wird Ihre Domain in Cloudflare verwaltet, lassen Sie die Einträge beim Holen des Zertifikats auf DNS only (graue Wolke). Sobald Sie es haben, können Sie auf Proxied umschalten; siehe Cloudflare und SSL-Modi.
Nginx installieren#
Funktioniert unter Ubuntu 24.04 und Debian 13:
apt update
apt -y install nginxapt -y install nginx– installiert Nginx. Der Dienst startet sofort und ist beim Booten des VPS aktiviert.
Prüfung: Öffnen Sie http://<VPS_IP_ADRESSE> in Ihrem Browser. Ersetzen Sie <VPS_IP_ADRESSE> durch die IPv4-Adresse aus der Registerkarte Netzwerk (zum Beispiel http://203.0.113.10). Sie sehen die Seite „Welcome to nginx!“.
Wie die Konfiguration aufgebaut ist#
| Pfad | Wofür |
|---|---|
/etc/nginx/nginx.conf | Die Hauptkonfiguration. Sie ändern sie in der Regel nicht. Sie lädt die Dateien aus den Verzeichnissen unten. |
/etc/ | Konfigurationen einzelner Websites (Server-Blöcke). Eine Datei hier existiert, ist aber nicht aktiv. |
/etc/ | Links (Symlinks) auf Dateien in sites-available. Nginx lädt nur, was hier liegt. |
/etc/nginx/conf.d/ | Zusätzliche Konfiguration; alle *.conf-Dateien werden geladen. |
/var/www/ | Der übliche Ort für Website-Dateien. |
/var/log/nginx/ | Logs: access.log (Anfragen) und error.log (Fehler). |
Eine Website aktivieren Sie also, indem Sie eine Datei in sites-available und einen Link darauf in sites-enabled erstellen. Sie deaktivieren sie, indem Sie den Link löschen.
Statische Website#
Eine statische Website ist eine Sammlung fertiger HTML-, CSS-, JavaScript- und Bilddateien.
Legen Sie das Website-Verzeichnis und eine Testseite an:
bash mkdir -p /var/www/example.cz/html echo '<h1>Es funktioniert</h1>' > /var/www/example.cz/html/index.htmlmkdir -p– legt das Verzeichnis einschließlich übergeordneter an.echo '…' > …/index.html– schreibt eine einfache Seite inindex.html.
Laden Sie später Ihre eigenen Website-Dateien per SFTP (Anleitung) oder in der Registerkarte Dateien in dieses Verzeichnis hoch.
Erstellen Sie die Website-Konfiguration:
bash nano /etc/nginx/sites-available/example.cz/etc/ nginx/ nginxsites-available/ example.cz server { listen 80; listen [::]:80; server_name example.cz www.example.cz; root /var/www/example.cz/html; index index.html; location / { try_files $uri $uri/ =404; } }
| Direktive | Was sie tut | Wann ändern |
|---|---|---|
listen 80; | Lauscht auf Port 80 für IPv4. | Nie (Certbot ergänzt HTTPS). |
listen [::]:80; | Dasselbe für IPv6. | Löschen, wenn der VPS kein IPv6 hat. |
server_name example.cz www.example.cz; | Die Domains, für die dieser Block gilt. Nginx wählt danach die Website aus. | Tragen Sie Ihre Domains durch Leerzeichen getrennt ein. |
root /var/ | Das Verzeichnis mit den Website-Dateien. | Wenn die Dateien woanders liegen. |
index index.html; | Welche Datei gesendet wird, wenn die URL mit / endet. | Bei PHP-Websites index.php index.html. |
location / { … } | Regeln für alle URLs der Website. | – |
try_files $uri $uri/ =404; | Versucht, eine Datei zu finden, dann ein Verzeichnis; existiert keines von beiden, liefert sie einen 404-Fehler. | Bei Single-Page-Apps (React, Vue) try_files $uri $uri/ /index.html;. |
Aktivieren Sie die Website, deaktivieren Sie die Standard-Willkommensseite, testen Sie die Konfiguration und laden Sie sie:
bash ln -s /etc/nginx/sites-available/example.cz /etc/nginx/sites-enabled/ rm /etc/nginx/sites-enabled/default nginx -t systemctl reload nginxln -s … /etc/– erstellt den Link, sodass Nginx die Website lädt.nginx/ sites-enabled/ rm /etc/– deaktiviert die Standard-Website (die Datei bleibt innginx/ sites-enabled/ default sites-available).nginx -t– prüft die Syntax aller Konfigurationen. Es muss mitsyntax is okundtest is successfulenden.systemctl reload nginx– lädt die neue Konfiguration, ohne Verbindungen abzubrechen.
Prüfung: http://example.cz zeigt die Überschrift „Es funktioniert“ von der Testseite.
Reverse Proxy für eine App#
Eine App (Node.js, Python, Java, ein Docker-Container) läuft in der Regel auf einem eigenen Port, zum Beispiel 3000. Sie setzen Nginx als Reverse Proxy davor: Er nimmt Anfragen auf Port 80/443 entgegen und leitet sie an die App weiter. Vorteile: Nginx übernimmt HTTPS, die App muss nicht aus dem Internet sichtbar sein, und ein VPS kann mehrere Apps unter verschiedenen Domains betreiben.
Lassen Sie die App nur auf 127.0.0.1 lauschen
Konfigurieren Sie die App so, dass sie auf 127.0.0.1:3000 lauscht, nicht auf 0.0.0.0:3000. Dann ist sie nur über Nginx erreichbar, und niemand kann ihn umgehen, indem er sich direkt mit dem Port verbindet. Bei Docker veröffentlichen Sie den Port als 127.0.0.1:3000:3000 (siehe Docker und Docker Compose).
Konfiguration (statt der statischen Website oder als weitere Datei für eine andere Domain, zum Beispiel app.example.cz):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name app.example.cz;
client_max_body_size 20m;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 300s;
}
}| Direktive | Was sie tut | Wann ändern |
|---|---|---|
map $http_upgrade $connection_upgrade { … } | Eine Hilfsvariable für Websockets: Wenn der Browser ein Upgrade der Verbindung verlangt, wird upgrade gesendet, sonst close. Sie wird in der gesamten Konfiguration nur einmal benötigt (eine zweite Kopie lässt nginx -t ein Duplikat melden). | Nie. |
client_max_body_size 20m; | Die maximale Größe einer hochgeladenen Anfrage. Der Standard ist 1 MB; größere Uploads scheitern mit Fehler 413. | Je nachdem, was die App akzeptiert. |
proxy_pass http://127.0.0.1:3000; | Wohin Anfragen weitergeleitet werden. | Der Port Ihrer App. |
proxy_http_version 1.1; | Spricht mit der App über HTTP/1.1. Ältere Nginx-Versionen verwenden sonst HTTP/1.0, das mit Websockets nicht funktioniert. | Nie. |
proxy_set_header Host $host; | Die App sieht die ursprüngliche Domain aus dem Browser. | Nie. |
proxy_set_header X-Real-IP …, X-Forwarded-For … | Die App sieht die echte IP-Adresse des Besuchers statt 127.0.0.1. | Nie. |
proxy_set_header X-Forwarded-Proto $scheme; | Die App weiß, dass der Besucher über HTTPS kam (wichtig für Weiterleitungen und Cookies). | Nie. |
Upgrade, Connection | Websocket-Anfragen durchreichen (Chat, Live-Updates, Socket.IO). | Nie. |
proxy_read_timeout 300s; | Wie lange auf die Antwort der App gewartet wird. Der Standard von 60 s schließt untätige Websockets. | Erhöhen für lang laufende Vorgänge. |
Das Aktivieren funktioniert wie bei der statischen Website:
ln -s /etc/nginx/sites-available/app.example.cz /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginxDamit die App dauerhaft läuft und nach einem Neustart des VPS automatisch startet, richten Sie sie als Dienst ein: Eine App rund um die Uhr mit systemd betreiben.
HTTPS mit Certbot#
Certbot installieren#
apt -y install certbot python3-certbot-nginxcertbot– der Let's-Encrypt-Client.python3-certbot-nginx– ein Plugin, das die Domain über Nginx prüfen und dessen Konfiguration selbst bearbeiten kann.
Certbot aus snap
Die Certbot-Autoren empfehlen die Installation über snap, das immer die neueste Version hat. Das Distributionspaket (Ubuntu 24.04 hat Certbot 2.9, Debian 13 hat 4.0) funktioniert für den normalen Gebrauch genauso und wird mit Ihren übrigen Paketen aktualisiert. Möchten Sie snap, folgen Sie certbot.eff.org/instructions (öffnet sich in einem neuen Fenster) und deinstallieren Sie zuerst die apt-Pakete, damit Sie nicht zwei Certbots haben.
Zertifikat holen#
certbot --nginx -d example.cz -d www.example.cz--nginx– Prüfung über Nginx und automatische Bearbeitung seiner Konfiguration.-d example.cz -d www.example.cz– die Domains, für die Sie das Zertifikat möchten. Jede muss einen auf den VPS zeigenden DNS-Eintrag haben und imserver_nameeiner Website stehen.
Beim ersten Lauf stellt Certbot diese Fragen:
| Frage | Was antworten | Warum |
|---|---|---|
E-Mail (Enter email address…) | Ihre Adresse | Let's Encrypt sendet wichtige Hinweise dorthin. Seit 2025 sendet es keine Ablauferinnerungen mehr; die Erneuerung läuft automatisch. |
Please read the Terms of Service… Do you agree? | Y | Ohne Zustimmung zu den Bedingungen erhalten Sie kein Zertifikat. |
Would you be willing… to share your email address with the Electronic Frontier Foundation… | Y oder N | Nur für EFF-Neuigkeiten; hat keine Auswirkung auf das Zertifikat. |
Ergebnis: Successfully deployed certificate for example.cz to /etc/ und Congratulations! You have successfully enabled HTTPS….
Was Certbot in der Konfiguration geändert hat#
Öffnen Sie die Website-Konfiguration (nano /etc/). Zeilen mit der Markierung # managed by Certbot hat Certbot hinzugefügt:
| Hinzugefügte Zeile | Was sie tut |
|---|---|
listen 443 ssl; | Die Website lauscht auch auf Port 443 mit TLS. |
listen [::]:443 ssl ipv6only=on; | Dasselbe für IPv6. Certbot fügt sie hinzu, wenn der Block listen [::]:80 enthält. Die Option ipv6only=on (auf IPv6 nur für IPv6-Verbindungen lauschen) fügt es nur bei der ersten Website auf dem VPS hinzu; bei den anderen steht die Zeile ohne sie. |
ssl_certificate /etc/ | Das Zertifikat (einschließlich Zwischenzertifikaten). |
ssl_certificate_key /etc/ | Der private Schlüssel des Zertifikats. Geben Sie ihn nie weiter. |
include /etc/ | Die von Certbot empfohlenen TLS-Einstellungen (Protokolle, Cipher). |
ssl_dhparam /etc/ | Parameter für den Schlüsselaustausch. |
ein neuer server-Block mit return 301 https://… | Leitet HTTP auf HTTPS um. Certbot fügt ihn automatisch hinzu. |
Bearbeiten Sie diese Zeilen nicht von Hand, es sei denn, Sie wissen genau, warum. Certbot verlässt sich bei der Erneuerung darauf.
Automatische Erneuerung#
Ein Let's-Encrypt-Zertifikat ist 90 Tage gültig (Stand Oktober 2026; Let's Encrypt verkürzt die Gültigkeit schrittweise). Das Certbot-Paket hat einen Timer installiert, der die Zertifikate zweimal täglich prüft und diejenigen erneuert, die noch etwa ein Drittel ihrer Gültigkeit haben. Das Nginx-Plugin lädt Nginx nach einer Erneuerung von selbst neu.
Prüfen Sie, dass der Timer existiert und die Erneuerung funktioniert:
systemctl list-timers | grep certbot
certbot renew --dry-runsystemctl list-timers | grep certbot– gibt eine Zeile mitcertbot.timerund dem Zeitpunkt des nächsten Laufs aus.certbot renew --dry-run– probiert eine Erneuerung gegen den Staging-Server, ohne die echten Zertifikate anzufassen. Es muss mitCongratulations, all simulated renewals succeededenden.
certbot certificates listet Ihre Zertifikate und ihre Gültigkeit auf.
Limits von Let's Encrypt
Let's Encrypt stellt für denselben Satz von Domains höchstens 5 Zertifikate innerhalb von 7 Tagen aus und sperrt Sie nach 5 fehlgeschlagenen Domain-Prüfungen innerhalb einer Stunde vorübergehend. Verwenden Sie bei der Fehlersuche --dry-run (Staging-Server) und führen Sie den echten Befehl erst aus, wenn der Test besteht.
Cloudflare und SSL-Modi#
Läuft Ihre Website über den Cloudflare-Proxy (orangefarbene Wolke), sind zwei Verbindungen verschlüsselt: zwischen Besucher und Cloudflare sowie zwischen Cloudflare und Ihrem VPS. Die zweite steuert der Modus SSL/TLS in Cloudflare:
| Modus | Verbindung von Cloudflare zum VPS | Empfehlung |
|---|---|---|
| Off | Keine Verschlüsselung | Nicht verwenden. |
| Flexible | Unverschlüsseltes HTTP zu Port 80 | Nicht verwenden. In Kombination mit der HTTPS-Weiterleitung in Nginx entsteht eine Endlosschleife (ERR_TOO_MANY_REDIRECTS). |
| Full | Dasselbe Protokoll wie der Besucher; bei HTTPS wird das VPS-Zertifikat nicht geprüft | Nur vorübergehend. |
| Full (strict) | Dasselbe Protokoll wie der Besucher; bei HTTPS wird das VPS-Zertifikat geprüft | Empfohlen. Ihr Let's-Encrypt-Zertifikat ist gültig, also funktioniert es. |
Führen Sie nach dem Umschalten auf Proxied certbot renew --dry-run aus und prüfen Sie, dass die Erneuerung auch über den Cloudflare-Proxy funktioniert. Scheitert sie, stellen Sie sicher, dass Sie Port 80 nicht mit eigenen Regeln in Cloudflare blockieren. Eine dauerhafte Lösung für Fortgeschrittene ist die Domain-Prüfung über DNS mit dem Plugin python3-certbot-dns-cloudflare (siehe die Certbot-Dokumentation (öffnet sich in einem neuen Fenster)).
Grundlegende Absicherung#
Nginx-Version verbergen. Unter Debian 13 enthält
/etc/nginx/nginx.confstandardmäßig bereitsserver_tokens off;. Öffnen Sie unter Ubuntu 24.04 die Datei (nano /etc/nginx/nginx.conf) und entfernen Sie im Blockhttpdas#vor# server_tokens off;. Nginx verrät seine Version dann weder in Headern noch auf Fehlerseiten.Sicherheits-Header. Diese können Sie dem
server-Block mit Port 443 hinzufügen:nginx 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;X-Content-Type-Options nosniff– der Browser rät den Dateityp nicht (Schutz vor getarnten Skripten).X-Frame-Options SAMEORIGIN– die Website lässt sich nicht in einem Frame in eine fremde Seite einbetten (Schutz vor Clickjacking).Referrer-Policy strict-origin-when-cross-origin– andere Websites erhalten nur die Domain, nicht die vollständige Seiten-URL.always– der Header wird auch bei Fehlerantworten gesendet.
Führen Sie nach jeder Änderung
nginx -tundsystemctl reload nginxaus.
Überlegen Sie, bevor Sie HSTS aktivieren
Der Header Strict-Transport-Security (HSTS) weist Browser an, für die Domain nur HTTPS zu verwenden, auch Monate im Voraus. Funktioniert HTTPS dann nicht mehr (abgelaufenes Zertifikat, Umzug der Domain), lädt die Website nicht. Schalten Sie ihn erst ein, wenn HTTPS zuverlässig läuft.
Überprüfung#
curl -I http://example.cz
curl -I https://example.czcurl -I– lädt nur die Antwort-Header herunter.
Der erste Befehl liefert HTTP/1.1 301 Moved Permanently mit dem Header Location: https://example.cz/, der zweite HTTP/1.1 200 OK (oder HTTP/2 200). Im Browser sehen Sie ein Schloss neben der Adresse.
Fehlerbehebung#
nginx -t meldet einen Fehler#
Die Ausgabe enthält Datei und Zeilennummer, zum Beispiel unexpected "}" in /etc/. Meist fehlt ein Semikolon am Ende einer Direktive, oder eine geschweifte Klammer ist zu viel oder fehlt. Korrigieren Sie die Zeile und führen Sie den Test erneut aus. Starten Sie Nginx mit einer kaputten Konfiguration nicht neu; es würde nicht starten.
502 Bad Gateway#
Nginx läuft, aber die App dahinter antwortet nicht. Prüfen Sie, dass die App läuft und auf dem Port aus proxy_pass lauscht:
ss -tlnp | grep 3000ss -tlnp– lauschende TCP-Ports mit Programmnamen.
Wird nichts ausgegeben, läuft die App nicht oder lauscht woanders. Details stehen in /var/log/nginx/error.log (eine Zeile mit connect() failed (111: Connection refused)).
504 Gateway Time-out#
Die App hat zu spät geantwortet. Erhöhen Sie proxy_read_timeout oder finden Sie heraus, warum die App langsam ist.
413 Request Entity Too Large#
Die hochgeladene Datei ist größer als client_max_body_size (Standard 1 MB). Erhöhen Sie den Wert im server-Block und laden Sie die Konfiguration neu.
Statt Ihrer Website erscheint „Welcome to nginx!“#
Die Standard-Website ist noch aktiv, oder die Domain in server_name passt nicht zur Adresse im Browser. Löschen Sie /etc/ und prüfen Sie server_name.
Nginx startet nicht: Address already in use#
Port 80 oder 443 ist von einem anderen Programm belegt, typischerweise Apache. Finden Sie heraus, von welchem: ss -tlnp | grep -E ':80 |:443 '. Stoppen und deaktivieren Sie Apache (systemctl disable --now apache2) oder deinstallieren Sie es.
Certbot: Could not automatically find a matching server block#
Keine Website hat die Domain, für die Sie ein Zertifikat anfordern, in ihrem server_name. Fügen Sie sie zu server_name hinzu, führen Sie nginx -t, systemctl reload nginx und Certbot erneut aus.
Certbot: Timeout during connect (likely firewall problem) oder Connection refused#
Let's Encrypt konnte Port 80 nicht erreichen. Prüfen Sie das DNS (getent hosts example.cz), die Firewall im Kundenbereich und UFW. Hat die Domain einen AAAA-Eintrag, muss er auf die IPv6-Adresse dieses VPS zeigen, und die Website muss auch auf IPv6 lauschen (listen [::]:80;).
Cloudflare: Fehler 521, 522, 525 oder 526#
- 521 – der VPS hat die Verbindung abgelehnt: Nginx läuft nicht, oder die Firewall blockiert Port 443.
- 522 – die Verbindung ist abgelaufen: Der VPS ist unerreichbar, oder die Firewall verwirft Pakete.
- 525 – der TLS-Handshake ist fehlgeschlagen: Nginx hat keine gültige HTTPS-Konfiguration auf Port 443.
- 526 – das Zertifikat auf dem VPS ist ungültig oder abgelaufen, im Modus Full (strict). Prüfen Sie
certbot certificates.
ERR_TOO_MANY_REDIRECTS#
Typischerweise Cloudflare im Modus Flexible in Kombination mit der HTTPS-Weiterleitung. Schalten Sie Cloudflare auf Full (strict).
Zusammenfassung
- Websites liegen in
/etc/; aktiv sind nur die mit einem Link innginx/ sites-available/ sites-enabled. - Nach jeder Änderung:
nginx -tundsystemctl reload nginx. - Lassen Sie die App auf
127.0.0.1lauschen und stellen Sie sie über Nginx mitproxy_passbereit, einschließlich derX-Forwarded-*-Header. - Holen Sie ein Zertifikat mit
certbot --nginx -d …; es erneuert sich selbst, und Sie prüfen das mitcertbot renew --dry-run. - Verwenden Sie mit Cloudflare den Modus Full (strict), nie Flexible.
Weiterführende Links#
- nginx: Beginner's Guide (öffnet sich in einem neuen Fenster)
- nginx: WebSocket proxying (öffnet sich in einem neuen Fenster)
- nginx: ngx_http_proxy_module (öffnet sich in einem neuen Fenster)
- Certbot: User Guide (öffnet sich in einem neuen Fenster)
- Let's Encrypt: Rate Limits (öffnet sich in einem neuen Fenster)
- Cloudflare: SSL/TLS encryption modes (öffnet sich in einem neuen Fenster)