Zum Inhalt springen
Aktueller Gutschein:10 % RabattIn der Bestellung einlösen

Website auf einem VPS: Nginx und ein Let's-Encrypt-Zertifikat (HTTPS)

Installieren Sie Nginx, liefern Sie eine statische Website aus oder richten Sie einen Reverse Proxy für eine App (Node.js und andere) ein, und holen Sie ein kostenloses Let's-Encrypt-Zertifikat, das sich selbst erneuert.

  • Fortgeschrittene
  • ~25 Min.
  • Aktualisiert am 9.10.2026

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, optional AAAA); 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#

  1. Prüfen Sie, dass die Domain auf den VPS zeigt. Führen Sie auf dem VPS aus:

    bash
    getent hosts example.cz

    Sie 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.

  2. Ö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/tcp und ufw allow 443/tcp.

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:

bash
apt update
apt -y install nginx
  • apt -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#

PfadWofür
/etc/nginx/nginx.confDie Hauptkonfiguration. Sie ändern sie in der Regel nicht. Sie lädt die Dateien aus den Verzeichnissen unten.
/etc/nginx/sites-available/Konfigurationen einzelner Websites (Server-Blöcke). Eine Datei hier existiert, ist aber nicht aktiv.
/etc/nginx/sites-enabled/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.

  1. 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.html
    • mkdir -p – legt das Verzeichnis einschließlich übergeordneter an.
    • echo '…' > …/index.html – schreibt eine einfache Seite in index.html.

    Laden Sie später Ihre eigenen Website-Dateien per SFTP (Anleitung) oder in der Registerkarte Dateien in dieses Verzeichnis hoch.

  2. Erstellen Sie die Website-Konfiguration:

    bash
    nano /etc/nginx/sites-available/example.cz
    /etc/nginx/sites-available/example.cznginx
    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;
        }
    }
DirektiveWas sie tutWann ä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/www/example.cz/html;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;.
  1. 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 nginx
    • ln -s … /etc/nginx/sites-enabled/ – erstellt den Link, sodass Nginx die Website lädt.
    • rm /etc/nginx/sites-enabled/default – deaktiviert die Standard-Website (die Datei bleibt in sites-available).
    • nginx -t – prüft die Syntax aller Konfigurationen. Es muss mit syntax is ok und test is successful enden.
    • 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):

/etc/nginx/sites-available/app.example.cznginx
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;
    }
}
DirektiveWas sie tutWann ä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, ConnectionWebsocket-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:

bash
ln -s /etc/nginx/sites-available/app.example.cz /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

Damit 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#

bash
apt -y install certbot python3-certbot-nginx
  • certbot – 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#

bash
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 im server_name einer Website stehen.

Beim ersten Lauf stellt Certbot diese Fragen:

FrageWas antwortenWarum
E-Mail (Enter email address…)Ihre AdresseLet'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?YOhne Zustimmung zu den Bedingungen erhalten Sie kein Zertifikat.
Would you be willing… to share your email address with the Electronic Frontier Foundation…Y oder NNur für EFF-Neuigkeiten; hat keine Auswirkung auf das Zertifikat.

Ergebnis: Successfully deployed certificate for example.cz to /etc/nginx/sites-enabled/example.cz und Congratulations! You have successfully enabled HTTPS….

Was Certbot in der Konfiguration geändert hat#

Öffnen Sie die Website-Konfiguration (nano /etc/nginx/sites-available/example.cz). Zeilen mit der Markierung # managed by Certbot hat Certbot hinzugefügt:

Hinzugefügte ZeileWas 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/letsencrypt/live/example.cz/fullchain.pem;Das Zertifikat (einschließlich Zwischenzertifikaten).
ssl_certificate_key /etc/letsencrypt/live/example.cz/privkey.pem;Der private Schlüssel des Zertifikats. Geben Sie ihn nie weiter.
include /etc/letsencrypt/options-ssl-nginx.conf;Die von Certbot empfohlenen TLS-Einstellungen (Protokolle, Cipher).
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;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:

bash
systemctl list-timers | grep certbot
certbot renew --dry-run
  • systemctl list-timers | grep certbot – gibt eine Zeile mit certbot.timer und 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 mit Congratulations, all simulated renewals succeeded enden.

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:

ModusVerbindung von Cloudflare zum VPSEmpfehlung
OffKeine VerschlüsselungNicht verwenden.
FlexibleUnverschlüsseltes HTTP zu Port 80Nicht verwenden. In Kombination mit der HTTPS-Weiterleitung in Nginx entsteht eine Endlosschleife (ERR_TOO_MANY_REDIRECTS).
FullDasselbe Protokoll wie der Besucher; bei HTTPS wird das VPS-Zertifikat nicht geprüftNur vorübergehend.
Full (strict)Dasselbe Protokoll wie der Besucher; bei HTTPS wird das VPS-Zertifikat geprüftEmpfohlen. 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#

  1. Nginx-Version verbergen. Unter Debian 13 enthält /etc/nginx/nginx.conf standardmäßig bereits server_tokens off;. Öffnen Sie unter Ubuntu 24.04 die Datei (nano /etc/nginx/nginx.conf) und entfernen Sie im Block http das # vor # server_tokens off;. Nginx verrät seine Version dann weder in Headern noch auf Fehlerseiten.

  2. 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.
  3. Führen Sie nach jeder Änderung nginx -t und systemctl reload nginx aus.

Ü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#

bash
curl -I http://example.cz
curl -I https://example.cz
  • curl -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/nginx/sites-enabled/example.cz:12. 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:

bash
ss -tlnp | grep 3000
  • ss -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/nginx/sites-enabled/default 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/nginx/sites-available/; aktiv sind nur die mit einem Link in sites-enabled.
  • Nach jeder Änderung: nginx -t und systemctl reload nginx.
  • Lassen Sie die App auf 127.0.0.1 lauschen und stellen Sie sie über Nginx mit proxy_pass bereit, einschließlich der X-Forwarded-*-Header.
  • Holen Sie ein Zertifikat mit certbot --nginx -d …; es erneuert sich selbst, und Sie prüfen das mit certbot renew --dry-run.
  • Verwenden Sie mit Cloudflare den Modus Full (strict), nie Flexible.

War diese Anleitung hilfreich?

Gilt für

  • Ubuntu 24.04
  • Debian 13

Dienste

  • VPS
  • Domains & DNS
Fehler in der Anleitung melden

Anleitungen durchsuchen