Ein SSL-Zertifikat erstellen kostet heute kein Geld und dauert auf einem eigenen Server wenige Sekunden. Der Standardweg ist Let’s Encrypt. Mit nginx installierst du certbot und rufst sudo certbot --nginx -d example.com -d www.example.com auf. certbot beweist, dass dir die Domain gehört, holt das Zertifikat, trägt es in die Konfiguration ein und richtet die automatische Erneuerung ein. Noch einfacher geht es mit Caddy als Webserver: Dort schreibst du nur den Domainnamen in die Konfiguration, alles andere passiert von allein.
Das ist die kurze Antwort. Die längere ist wichtiger, denn beim Erstellen eines Zertifikats geht selten etwas schief. Ärger macht die Erneuerung drei Monate später. Ein Zertifikat ohne funktionierende Erneuerung ist eine Zeitbombe, und die meisten Fallen werfen keine Fehlermeldung. In unserem Test meldete certbot renew nach einem Serverumzug „No renewals were attempted“ mit Exit-Code 0, also Erfolg, obwohl gar kein Zertifikat mehr erneuert werden konnte. Und nach einer erfolgreichen Erneuerung lieferte nginx weiter das alte Zertifikat aus.
Wir haben das am 5. November 2026 auf unserem eigenen Server durchgespielt: mit certbot 2.9.0, nginx 1.24, Caddy 2.11.7 und Pebble, der offiziellen Test-Zertifizierungsstelle von Let’s Encrypt. Pebble spricht dasselbe ACME-Protokoll wie die echte CA, stellt aber Zertifikate aus, denen kein Browser vertraut. So konnten wir Fehler provozieren, ohne in die Limits von Let’s Encrypt zu laufen. Alle Zahlen in diesem Artikel stammen aus diesen Läufen. Was HTTPS grundsätzlich ist und was ein Zertifikat beweist, erklärt unser Artikel Was ist HTTPS?. Hier geht es ums Machen.
Video: alleswasinteressiert zeigt, wie Caddy HTTPS vollautomatisch einrichtet, im direkten Vergleich zu nginx mit certbot (September 2026).
SSL-Zertifikat erstellen: die vier Wege im Überblick
Bevor du einen Befehl tippst, lohnt sich die Frage, wofür das Zertifikat gedacht ist. Davon hängt ab, welcher Weg passt.
| Weg | Wofür | Aufwand | Kosten | Vertrauen im Browser |
|---|---|---|---|---|
| Caddy (automatisch) | neue Server, Reverse Proxy, Docker | 1 Zeile Konfiguration | 0 € | ja |
| certbot mit nginx oder Apache | bestehende nginx-/Apache-Server | 2 Befehle | 0 € | ja |
| certbot mit DNS-Challenge | Wildcard *.example.com, Server ohne offenen Port 80 | DNS-API-Zugang nötig | 0 € | ja |
| Selbstsigniert (openssl) | Tests, internes Netz, Entwicklung | 1 Befehl | 0 € | nein, Warnung |
| Gekauftes Zertifikat (OV/EV) | wenn Verträge oder Compliance es verlangen | Antrag, Prüfung | 50–500 €/Jahr | ja |
Für fast jede Website ist ein kostenloses Zertifikat von Let’s Encrypt die richtige Wahl. Technisch verschlüsselt es genauso stark wie ein gekauftes. Bezahlte Zertifikate prüfen zusätzlich die Organisation hinter der Domain, aber Browser zeigen diesen Unterschied seit Jahren nicht mehr an. Mehr dazu im Abschnitt über DV, OV und EV im HTTPS-Artikel.
Ein kurzes Wort zum Namen: Eigentlich gibt es seit Jahren kein „SSL“ mehr. Das Protokoll heißt TLS, und SSL ist aus Sicherheitsgründen überall abgeschaltet. Das Zertifikat ist aber dasselbe. Ob du „SSL-Zertifikat“, „TLS-Zertifikat“ oder „HTTPS-Zertifikat“ sagst, gemeint ist immer ein X.509-Zertifikat, das deinen Domainnamen mit einem öffentlichen Schlüssel verbindet.
Was vor dem Erstellen stimmen muss
Let’s Encrypt stellt ein Zertifikat nur aus, wenn du beweist, dass du die Domain kontrollierst. Das passiert automatisch über das ACME-Protokoll. Damit es klappt, müssen drei Dinge vorher stimmen:
- Der DNS-Eintrag zeigt auf deinen Server. Ein A-Record (IPv4) und gegebenenfalls ein AAAA-Record (IPv6) für jede Domain, die ins Zertifikat soll, also auch für
www. Wie das funktioniert, steht in Was ist DNS?. Achtung bei IPv6: Hat deine Domain einen AAAA-Record, prüft Let’s Encrypt bevorzugt über IPv6. Zeigt der Eintrag auf eine alte Adresse, scheitert die Prüfung, obwohl IPv4 stimmt. - Port 80 ist von außen erreichbar. Für die häufigste Prüfmethode (HTTP-01) ruft Let’s Encrypt eine Datei über einfaches HTTP ab. Eine Firewall, die nur 443 öffnet, blockiert das. In unserem Test mit geschlossenem Port lautete die Meldung
Type: connectionundconnect: connection refused. Welche Ports du auf einem frischen Server öffnen solltest, beschreibt unser Artikel Linux-Server einrichten. - Ein Webserver läuft schon, oder Port 80 ist frei, wenn certbot selbst kurz einen Server starten soll.
Wie die Domain-Prüfung funktioniert
Es gibt drei Prüfverfahren, im ACME-Jargon „Challenges“:
- HTTP-01: certbot legt eine Datei mit einem Zufallstoken unter
http://deine-domain/.well-known/acme-challenge/ab. Let’s Encrypt ruft sie ab. Passt der Inhalt, ist bewiesen, dass du den Webserver kontrollierst. Das ist der Standard. - DNS-01: Du legst einen TXT-Eintrag
_acme-challenge.deine-domainmit dem Token an. Das ist der einzige Weg für Wildcard-Zertifikate und funktioniert auch, wenn der Server gar nicht aus dem Internet erreichbar ist. - TLS-ALPN-01: Die Prüfung läuft über Port 443 mit einer speziellen TLS-Erweiterung. Caddy nutzt das gern; mit certbot spielt es kaum eine Rolle.

Ein Detail aus unserem Test, das viele überrascht: Die Prüfstelle fragt nicht nur einmal. Im Log unseres nginx standen für jede einzelne Prüfung drei Abrufe derselben Datei. Die echte Let’s Encrypt prüft zusätzlich von mehreren Orten der Welt aus („Multi-Perspective Validation“). Wenn dein Server Zugriffe aus bestimmten Ländern per Geo-Block sperrt, kann die Prüfung deshalb scheitern, obwohl sie von deinem Standort aus funktioniert.
Weg 1: SSL-Zertifikat erstellen mit certbot und nginx
Das ist der Klassiker auf Ubuntu und Debian. Installiere certbot und das nginx-Plugin:
sudo apt update
sudo apt install certbot python3-certbot-nginx
Dein nginx braucht einen server-Block mit dem richtigen server_name, auch wenn er noch nur auf Port 80 lauscht:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
}
Dann holst du das Zertifikat:
sudo certbot --nginx -d example.com -d www.example.com
certbot fragt beim ersten Mal nach einer E-Mail-Adresse und den Nutzungsbedingungen. Danach prüft es die Domain, speichert das Zertifikat unter /etc/letsencrypt/live/example.com/, ergänzt deinen server-Block um listen 443 ssl und die Zertifikatspfade, richtet eine Weiterleitung von HTTP auf HTTPS ein und lädt nginx neu. Mehr musst du nicht tun.
Wer nginx als Reverse Proxy vor einer App betreibt, findet die passende Konfiguration im Artikel nginx als Reverse Proxy. Für Apache ist der Ablauf identisch, nur mit python3-certbot-apache und certbot --apache.
Wenn certbot die Konfiguration nicht anfassen soll: Webroot
Manche Admins wollen nicht, dass ein Werkzeug in ihre nginx-Konfiguration schreibt. Dafür gibt es certonly mit dem Webroot-Modus. certbot legt die Prüfdatei dann nur in einem Ordner ab, den dein Webserver ausliefert:
sudo certbot certonly --webroot -w /var/www/example \
-d example.com -d www.example.com
Danach trägst du die Pfade selbst ein:
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/example;
}
In unserem Test dauerte das komplette Ausstellen per Webroot für zwei Namen 2,4 Sekunden, im Standalone-Modus 3,1 Sekunden. Die Zeit spielt also keine Rolle.
Bei Webroot gibt es zwei Fehler, die sehr unterschiedlich aussehen:
- Der Ordner existiert nicht: certbot bricht sofort ab mit
/var/www/falsch does not exist or is not a directory, ohne die CA überhaupt zu fragen. Das ist der harmlose Fall. - Der Ordner existiert, aber nginx liefert ihn nicht aus. Das passiert, wenn
-wauf einen anderen Ordner zeigt als dasrootimserver-Block, oder wenn einelocation-Regel/.well-known/auf die App umleitet. certbot schreibt die Datei, Let’s Encrypt bekommt einen 404, und die Meldung lautetType: unauthorizedmitreturned 404. Das klingt nach einem Rechteproblem, ist aber ein Pfadproblem.
Der zweite Fall ist tückisch, weil er beim ersten Mal oft funktioniert und erst bei der Erneuerung scheitert, nachdem jemand die nginx-Konfiguration umgebaut hat. Eine robuste Lösung ist eine eigene location nur für ACME, die unabhängig vom Rest der Seite immer auf denselben Ordner zeigt:
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
}
Mit -w /var/www/letsencrypt bei certbot funktioniert das dann für jede Seite auf dem Server, egal ob dahinter WordPress, eine Node-App oder ein Reverse Proxy steckt.
Standalone: wenn noch kein Webserver läuft
certbot certonly --standalone -d example.com startet für die Prüfung einen eigenen kleinen Webserver auf Port 80. Das ist praktisch für Server ohne Webserver, etwa einen Mailserver, der nur ein Zertifikat braucht. Der Haken kommt später: Bei der Erneuerung muss Port 80 wieder frei sein. Läuft dort inzwischen nginx, scheitert die Erneuerung. Genau das ist in unserem Probelauf passiert (im Test lief alles auf Port 5002, auf einem echten Server ist es Port 80):
Failed to renew certificate app.gmtest.example with error:
Could not bind TCP port 5002 because it is already in use by another process
Wenn du einmal Standalone gewählt hast und später einen Webserver installierst, stell die Erneuerung auf Webroot um oder ergänze --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx". Die zweite Variante bedeutet allerdings ein paar Sekunden Ausfall bei jeder Erneuerung.
Weg 2: SSL-Zertifikat erstellen mit Caddy, ganz ohne certbot
Caddy ist ein Webserver, der HTTPS selbst erledigt. Die komplette Konfiguration für eine Seite mit Zertifikat sieht so aus:
example.com {
root * /var/www/example
file_server
}
Oder als Reverse Proxy vor einer App:
app.example.com {
reverse_proxy localhost:3000
}
Beim Start holt Caddy das Zertifikat, leitet HTTP auf HTTPS um und erneuert rechtzeitig. Es gibt keinen Timer, keinen Hook und keinen Reload, den man vergessen kann. In unserem Test gegen die Test-CA lieferte Caddy 5,6 Sekunden nach dem Start die Seite per HTTPS aus, und ein Aufruf per HTTP bekam automatisch einen 308-Redirect auf HTTPS.
Auf unserem eigenen Server verwaltet Caddy über tausend Zertifikate, auch das von getmind.io. Unser Zertifikat zum Zeitpunkt dieses Artikels: ausgestellt von Let’s Encrypt (Zwischenzertifikat YE1) am 6. September 2026, gültig bis 5. Dezember 2026, also die vollen 90 Tage. Caddy erneuert, wenn noch ein Drittel der Laufzeit übrig ist. Wann du Caddy und wann nginx nehmen solltest, haben wir im Vergleich Caddy vs Nginx mit eigenen Benchmarks gemessen.
Zwei Dinge musst du bei Caddy trotzdem beachten:
- Das Datenverzeichnis ist wichtig. Caddy speichert Zertifikate und Schlüssel unter
~/.local/share/caddybzw./var/lib/caddy/.local/share/caddy. Läuft Caddy in Docker ohne Volume für/data, holt jeder Neustart des Containers ein neues Zertifikat, und du landest irgendwann im Rate Limit. In unserem Artikel Docker Compose Beispiele ist das Volume deshalb immer dabei. - Wildcards brauchen ein DNS-Modul. Für
*.example.commusst du Caddy mit dem Plugin deines DNS-Anbieters bauen (xcaddy build --with github.com/caddy-dns/cloudflare). Das Standard-Binary kann nur HTTP- und TLS-ALPN-Prüfungen.
Seit August 2025 gibt es übrigens auch für nginx einen offiziellen Weg ohne certbot: das Modul ngx_http_acme_module aus den Paketquellen von nginx.org. Es holt und erneuert Zertifikate direkt in nginx, kann nach unserem Kenntnisstand aber noch keine Wildcards. Wer Ubuntus nginx-Paket nutzt, hat das Modul nicht und bleibt bei certbot.
Weg 3: Wildcard-Zertifikat per DNS-Challenge erstellen
Ein Wildcard-Zertifikat *.example.com gilt für alle Subdomains der ersten Ebene. Das ist praktisch, wenn ständig neue Subdomains dazukommen. Let’s Encrypt stellt Wildcards nur über die DNS-Prüfung aus. Wir haben es mit HTTP-01 versucht, und certbot brach mit Exit-Code 1 ab:
Client with the currently selected authenticator does not support
any combination of challenges that will satisfy the CA.
Die Meldung verrät nicht, dass das Problem der Stern im Namen ist. Wer sie zum ersten Mal sieht, sucht leicht eine Stunde lang an der falschen Stelle.
Der richtige Weg ist ein DNS-Plugin für deinen Anbieter. Für Cloudflare:
sudo apt install python3-certbot-dns-cloudflare
# API-Token mit Recht "Zone:DNS:Edit" nur für diese Zone
sudo mkdir -p /root/.secrets
echo "dns_cloudflare_api_token = DEIN_TOKEN" | sudo tee /root/.secrets/cloudflare.ini
sudo chmod 600 /root/.secrets/cloudflare.ini
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'
Wichtig: *.example.com deckt example.com selbst nicht ab, und auch nicht a.b.example.com. Deshalb stehen im Befehl beide Namen. Die einfachen Anführungszeichen um den Stern verhindern, dass die Shell ihn als Dateimuster auflöst.
Gibt es für deinen DNS-Anbieter kein Plugin, helfen die manuellen Hooks. Das ist ein kleines Skript, das den TXT-Eintrag per API setzt (--manual-auth-hook) und wieder löscht (--manual-cleanup-hook). certbot übergibt den Domainnamen in $CERTBOT_DOMAIN und den Token in $CERTBOT_VALIDATION. In unserem Test mit dem DNS-Testserver von Pebble lief ein Wildcard plus Hauptdomain so in 2,4 Sekunden durch. Mit einem echten DNS-Anbieter dauert es länger, weil der Eintrag erst auf allen Nameservern ankommen muss. Die Plugins warten dafür standardmäßig 10 bis 60 Sekunden.
Ein Hinweis aus eigener Erfahrung: Ein DNS-API-Token, der die ganze Zone ändern darf, ist ein sehr mächtiger Schlüssel. Wer ihn hat, kann deine Domain auf einen fremden Server umleiten. Gib dem Token nur das Recht für die eine Zone, die er braucht, und speichere ihn mit Rechten 600. Uns ist es schon passiert, dass ein Token für eine bestimmte Zone schlicht nicht galt (Cloudflare-Fehler 9109). Dann bleibt nur HTTP-01 per Webroot, und das ist auch völlig in Ordnung.
Weg 4: Selbstsigniertes Zertifikat erstellen
Für ein internes Testsystem, eine Entwicklungsumgebung oder ein Gerät im Heimnetz, das nie aus dem Internet erreichbar ist, kannst du ein Zertifikat selbst ausstellen. Ein Befehl genügt:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
-keyout intern.key -out intern.crt -days 365 \
-subj "/CN=intern.local" \
-addext "subjectAltName=DNS:intern.local"
Das dauerte bei uns unter einer Hundertstelsekunde. Das Zertifikat verschlüsselt genauso gut wie eines von Let’s Encrypt, aber niemand außer dir bürgt dafür. Browser zeigen deshalb eine Warnung, und Programme wie curl verweigern die Verbindung (in unserem Test Exit-Code 60), bis du dem Zertifikat ausdrücklich vertraust, etwa mit curl --cacert intern.crt.
Die wichtigste Zeile ist -addext "subjectAltName=...". Moderne Browser schauen seit Chrome 58 (2017) nur noch in das Feld „Subject Alternative Name“ (SAN), nicht mehr in den alten „Common Name“. Unser Test zeigt, warum diese Falle so hartnäckig ist: curl 8.5 akzeptierte auch das Zertifikat ohne SAN, weil es noch auf den Common Name zurückfällt. Wer seine Konfiguration nur mit curl testet, sieht also grün, während Chrome mit NET::ERR_CERT_COMMON_NAME_INVALID abbricht.
Für Entwicklung am eigenen Rechner ist mkcert bequemer. Das Werkzeug legt eine lokale CA an, trägt sie in den Vertrauensspeicher von System und Browsern ein und stellt Zertifikate aus, die ohne Warnung funktionieren. Den Schlüssel dieser lokalen CA solltest du nie weitergeben, denn damit könnte jemand für jede beliebige Domain gültige Zertifikate auf deinem Rechner erzeugen.
Selbstsignierte Zertifikate für eine öffentliche Website sind keine Lösung. Besucher müssen eine Sicherheitswarnung wegklicken, und genau das trainiert sie darauf, Warnungen zu ignorieren.
cert.pem oder fullchain.pem? Die Kette, die man nicht sieht
Nach dem Ausstellen liegen in /etc/letsencrypt/live/example.com/ vier Dateien:
| Datei | Inhalt | Wofür |
|---|---|---|
cert.pem | nur dein Zertifikat | fast nie direkt |
chain.pem | nur das Zwischenzertifikat | OCSP-Stapling, manche Apps |
fullchain.pem | dein Zertifikat + Zwischenzertifikat | das hier in nginx, Apache, HAProxy |
privkey.pem | der private Schlüssel | geheim, Rechte 600 |
Ein Browser vertraut nur einer kleinen Liste von Stammzertifikaten. Dein Zertifikat ist aber nicht direkt von einem Stamm signiert, sondern von einem Zwischenzertifikat. Der Server muss dieses Zwischenglied mitliefern, sonst kann der Client die Kette nicht schließen.

Wir haben nginx absichtlich mit cert.pem statt fullchain.pem konfiguriert. Ergebnis mit curl: SSL certificate problem: unable to get local issuer certificate, Exit-Code 60. Mit fullchain.pem: sofort in Ordnung. Das Gemeine daran: Desktop-Browser bemerken den Fehler oft nicht, weil sie fehlende Zwischenzertifikate aus einem Cache nachladen oder selbst herunterladen. Die Seite sieht im eigenen Browser perfekt aus, während Apps, Skripte, Webhooks von Zahlungsanbietern und manche Android-Geräte die Verbindung ablehnen.
Prüfen kannst du die ausgelieferte Kette von außen mit:
openssl s_client -connect example.com:443 -servername example.com </dev/null \
| grep -E "^ ?[0-9] s:|Verify return"
Du solltest mindestens zwei Zeilen (0 s: für deine Domain, 1 s: für das Zwischenzertifikat) und am Ende Verify return code: 0 (ok) sehen. Bei getmind.io sind es vier Einträge, weil Let’s Encrypt zusätzlich ein Brückenzertifikat zur älteren Wurzel ISRG Root X2 mitschickt.
Video: MK_0x41 zeigt den dritten beliebten Weg, Nginx Proxy Manager in Docker mit Let’s-Encrypt-Zertifikaten über eine Weboberfläche, inklusive DNS-Challenge über Cloudflare (Oktober 2026).
Zertifikat erneuern: wo es wirklich schiefgeht
Ein Zertifikat von Let’s Encrypt gilt derzeit 90 Tage. certbot richtet bei der Installation über das Ubuntu-Paket einen systemd-Timer ein, der zweimal täglich certbot renew aufruft. Erneuert wird, wenn weniger als 30 Tage Restlaufzeit übrig sind. Den Timer siehst du mit:
systemctl list-timers | grep certbot
Wie solche Timer intern funktionieren und wie du eigene anlegst, steht in systemd-Service erstellen. Klingt nach „einrichten und vergessen“. In unseren Tests gab es aber drei Stellen, an denen die Automatik leise versagt.

Falle 1: Das neue Zertifikat liegt da, aber nginx liefert das alte aus
nginx liest Zertifikatsdateien nur beim Start oder Reload. Erneuert certbot im certonly-Modus, liegt das neue Zertifikat auf der Platte, nginx hält aber das alte im Speicher. Wir haben das gemessen: Nach certbot renew --force-renewal hatte die Datei die neue Seriennummer 67E4…, nginx lieferte weiter die alte 176E… aus. Das fällt erst auf, wenn das alte abläuft, also bis zu 30 Tage später.
Das --nginx-Plugin lädt nginx selbst neu. Bei certonly brauchst du einen Deploy-Hook:
sudo certbot certonly --webroot -w /var/www/letsencrypt -d example.com \
--deploy-hook "systemctl reload nginx"
Mit Hook kam in unserem Test die neue Seriennummer sofort bei nginx an. certbot speichert den Hook in der Datei /etc/letsencrypt/renewal/example.com.conf (als renew_hook), er läuft also bei jeder späteren Erneuerung mit. Für bestehende Zertifikate kannst du ihn global hinterlegen, als ausführbares Skript in /etc/letsencrypt/renewal-hooks/deploy/. Dasselbe gilt für alle anderen Dienste, die das Zertifikat nutzen: Postfix, Dovecot, HAProxy, eine Node-App. Jeder braucht seinen Reload.
Falle 2: Der Testlauf wartet zweieinhalb Minuten
Der wichtigste Befehl nach jeder Änderung ist der Probelauf:
sudo certbot renew --dry-run
Er spielt die Erneuerung gegen den Staging-Server von Let’s Encrypt durch, ohne echte Zertifikate zu ersetzen. In unserem Test hing er erst einmal 155 Sekunden ohne Ausgabe. Im Log stand der Grund: Non-interactive renewal: random delay of 152.6 seconds. certbot wartet bei Erneuerungen ohne Terminal absichtlich eine zufällige Zeit bis zu acht Minuten, damit nicht alle Server der Welt zur selben Sekunde bei Let’s Encrypt anklopfen. In einem Skript oder einer CI-Pipeline sieht das aus wie ein hängender Prozess. Für manuelle Tests aus Skripten gibt es --no-random-sleep-on-renew. Im Timer solltest du die Pause aber lassen.
Der Probelauf deckte bei uns übrigens sofort Falle 3 aus dem Standalone-Abschnitt auf. Von zwei Zertifikaten klappte eines, das andere scheiterte am belegten Port 80. Genau dafür ist er da.
Falle 3: Serverumzug, und certbot sagt „alles gut“
Das ist die teuerste Falle, und wir sind selbst hineingetreten. Bei einem Serverumzug wurde ein Zertifikat als Datei auf den neuen Server kopiert. Die Seite lief, das Schloss war grün. Was fehlte, war die Erneuerungskonfiguration. Drei Monate später lief das Zertifikat ab, und die Seite war einige Stunden lang nicht erreichbar.
Wir haben beide typischen Umzugsfehler nachgestellt:
- Nur den Ordner
live/kopiert (mitcp -rL, also echte Dateien): nginx kann die Dateien lesen, alles läuft.certbot renewmeldetNo renewals were attempted.und beendet sich mit Exit-Code 0.certbot certificatessagtNo certificates found.Für certbot existiert das Zertifikat nicht, weil die Datei unterrenewal/fehlt. Ein Monitoring, das nur den Exit-Code des Timers prüft, sieht hier nie einen Fehler. live/mitcp -akopiert: Die Dateien inlive/sind nur Verknüpfungen auf../../archive/. Ohnearchive/zeigen sie ins Leere, nginx startet gar nicht erst. Das ist der bessere Fehler, weil er sofort auffällt.

Unsere Regel seit diesem Ausfall:
- Beim Umzug Zertifikate auf dem neuen Server neu ausstellen. Das dauert Sekunden, sobald der DNS-Eintrag umgestellt ist.
- Wenn das nicht geht, das komplette Verzeichnis
/etc/letsencryptübertragen, mitarchive/,live/,renewal/,accounts/undrenewal-hooks/, zum Beispiel mittaroderrsync -a. Pfade im Webroot und Hooks danach prüfen. - Danach immer
certbot certificates(muss das Zertifikat auflisten) undcertbot renew --dry-run(muss „success“ melden). - Das Ablaufdatum unabhängig von certbot überwachen (siehe nächster Abschnitt).
Ablauf überwachen: ein Check, der nicht von certbot abhängt
Ein Monitoring, das certbot fragt, ob certbot funktioniert, findet genau die Fehler nicht, die certbot selbst nicht bemerkt. Wir prüfen deshalb von außen, was der Server tatsächlich ausliefert:
#!/bin/bash
# cert-check.sh: warnt, wenn ein ausgeliefertes Zertifikat bald abläuft
set -euo pipefail
WARN_DAYS=14
for host in example.com www.example.com shop.example.com; do
end=$(echo | timeout 10 openssl s_client -connect "$host:443" -servername "$host" 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2) || { echo "FEHLER $host: keine Verbindung"; continue; }
days=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
if [ "$days" -lt "$WARN_DAYS" ]; then
echo "WARNUNG $host: nur noch $days Tage"
fi
done
Lass das Skript einmal täglich laufen und schick die Ausgabe dorthin, wo du sie liest: per Mail, Messenger oder in dein Monitoring. Wie du einen täglichen Job anlegst, erklärt Cronjob erstellen. Die Schwelle von 14 Tagen ist bewusst unter den 30 Tagen, ab denen certbot erneuert: Wenn die Warnung kommt, hat die Automatik schon zwei Wochen lang versagt, und du hast noch zwei Wochen Zeit.
Für eine einzelne Datei auf der Platte gibt es eine kürzere Variante: openssl x509 -in fullchain.pem -noout -checkend 1209600 liefert Exit-Code 1, wenn das Zertifikat in den nächsten 14 Tagen abläuft. Bedenke aber Falle 1: Die Datei kann neu sein, während der Server noch das alte Zertifikat ausliefert. Deshalb prüfen wir lieber über das Netz.
2027: Let’s Encrypt wechselt auf 64 Tage
Wer heute ein SSL-Zertifikat erstellt, sollte die nächsten Änderungen kennen. Let’s Encrypt hat am 7. Oktober 2026 angekündigt: Ab dem 10. Februar 2027 sind alle neuen Zertifikate standardmäßig nur noch 64 Tage gültig. Bis Februar 2028 sinkt die Laufzeit weiter auf 45 Tage. Grund ist eine Branchenregel, nach der ab März 2029 kein öffentliches Zertifikat länger als 47 Tage gelten darf. Gleichzeitig verkürzt Let’s Encrypt die Zeit, in der eine bestätigte Domain-Prüfung wiederverwendet werden darf, von 30 auf 10 Tage.
Was das für dich heißt:
- Mit Caddy oder certbot im Standard musst du nichts tun. Beide erneuern relativ zur Laufzeit bzw. lange genug vorher. certbot erneuert ab 30 Tagen Restlaufzeit, bei 64 Tagen also nach rund 34 Tagen.
- Fest eingetragene Zeiträume musst du anpassen. Let’s Encrypt empfiehlt ausdrücklich, Skripte, Cronjobs und Betriebsanleitungen nach Zahlen wie 60, 80 oder 83 Tagen zu durchsuchen. Ein Cronjob, der „alle 60 Tage erneuern“ macht, erneuert bei 64-Tage-Zertifikaten zu spät, sobald etwas einmal hakt.
- Halbautomatische Wege werden doppelt so oft fällig. Wer Zertifikate heute noch per Hand in ein Hosting-Panel, eine Firewall oder ein Gerät kopiert, macht das bald alle paar Wochen. Spätestens jetzt lohnt es sich, das zu automatisieren.
- Moderne Clients fragen die CA, wann sie erneuern sollen. Das Verfahren heißt ACME Renewal Information (ARI). Let’s Encrypt kann einem Client damit sagen „bitte jetzt erneuern“, etwa wenn Zertifikate vorzeitig widerrufen werden müssen. Ob dein Client ARI kann, hängt von der Version ab. Ubuntu 24.04 liefert certbot 2.9.0 aus; neuere Versionen gibt es über Snap.
Auch dein Überwachungsskript sollte die Schwelle relativ wählen. Bei 45-Tage-Zertifikaten sind 14 Tage Warnzeit fast ein Drittel der Laufzeit, das passt weiterhin.
Rate Limits: warum du zum Testen Staging nimmst
Let’s Encrypt begrenzt, wie viele Zertifikate du in welcher Zeit holen kannst. Für normale Websites spielt das keine Rolle, beim Ausprobieren aber schon. Die wichtigsten Grenzen nach der offiziellen Dokumentation:
- 50 Zertifikate pro registrierter Domain (z. B.
example.cominklusive aller Subdomains) innerhalb von 7 Tagen, - 5 Zertifikate für exakt dieselbe Namensliste innerhalb von 7 Tagen,
- 300 neue Bestellungen pro Konto innerhalb von 3 Stunden,
- 5 fehlgeschlagene Prüfungen pro Name und Stunde.
Ein abgelaufenes Limit kann Let’s Encrypt nicht zurücksetzen, es füllt sich nur langsam wieder auf. Wer zehnmal hintereinander mit einer falschen nginx-Konfiguration ein echtes Zertifikat anfordert, sperrt sich also für eine Weile aus. Zum Ausprobieren gibt es deshalb --test-cert (Staging-Server, Zertifikate ohne Browser-Vertrauen, viel höhere Limits) und --dry-run. Erst wenn das klappt, holst du das echte Zertifikat. Genau aus diesem Grund haben wir für diesen Artikel komplett gegen die Test-CA Pebble gearbeitet.
Die zweite häufige Ursache für Limits ist Docker ohne persistentes Volume. Jeder Neustart eines Containers holt dann ein neues Zertifikat für dieselben Namen, und nach fünf Neustarts in einer Woche ist Schluss.
Häufige Fehlermeldungen beim Erstellen und was sie bedeuten
| Meldung | Ursache | Lösung |
|---|---|---|
Type: connection, connection refused / Timeout during connect | Port 80 zu oder Firewall blockiert | Port 80 öffnen (ufw allow 80), Cloud-Firewall prüfen |
Type: unauthorized, returned 404 | Prüfdatei liegt nicht dort, wo der Webserver ausliefert | -w und root vergleichen, eigene ACME-location |
Type: dns, NXDOMAIN | DNS-Eintrag fehlt oder ist noch nicht angekommen | A/AAAA-Record prüfen, warten |
does not exist or is not a directory | falscher Webroot-Pfad | Pfad korrigieren |
Could not bind TCP port 80 | Standalone-Modus, aber Webserver läuft | auf Webroot oder Plugin umstellen |
does not support any combination of challenges | Wildcard mit HTTP-Prüfung | DNS-Plugin verwenden |
too many certificates already issued | Rate Limit | warten, künftig --test-cert |
Browser: ERR_CERT_COMMON_NAME_INVALID | Name fehlt im Zertifikat (SAN) | alle Namen mit -d angeben, bei Eigenbau SAN setzen |
curl: unable to get local issuer certificate | Server liefert keine Kette | fullchain.pem statt cert.pem |
Bei Fehlern lohnt immer ein Blick in /var/log/letsencrypt/letsencrypt.log. Dort steht die vollständige Antwort der CA, inklusive der URL, die sie aufrufen wollte.
Sicherheit: den privaten Schlüssel schützen
Das Zertifikat selbst ist öffentlich, jeder Besucher bekommt es. Geheim ist nur privkey.pem. Wer den Schlüssel hat, kann sich als deine Website ausgeben. certbot legt ihn mit Rechten 600 an, die Ordner live/ und archive/ mit 700. In unserem Test erzeugte certbot 2.9 standardmäßig einen ECDSA-Schlüssel mit der Kurve P-256. Der ist kleiner und schneller als RSA und wird von allen aktuellen Clients unterstützt.
Ein paar Regeln, die sich bei uns bewährt haben:
- Den Schlüssel nie per Chat, Mail oder in ein Git-Repository kopieren. Wenn ein Dienst unter einem anderen Benutzer den Schlüssel lesen muss, lieber mit Gruppen arbeiten oder per Deploy-Hook eine Kopie mit passenden Rechten erstellen.
/etc/letsencryptgehört ins Backup, aber verschlüsselt. Wie wir Backups aufbauen, steht in der 3-2-1-Backup-Strategie.- Bei Verdacht auf einen gestohlenen Schlüssel: Zertifikat widerrufen (
certbot revoke --cert-name example.com) und neu ausstellen. - HTTPS allein schützt nicht vor allem. Was danach kommt, etwa HSTS und Weiterleitungen, steht im Artikel Was ist HTTPS?, und gegen automatisierte Angriffe auf Logins hilft Fail2ban.
Häufige Fragen
Wie kann ich ein SSL-Zertifikat erstellen?
Am einfachsten mit Let’s Encrypt. Auf einem Server mit nginx installierst du certbot und python3-certbot-nginx und rufst sudo certbot --nginx -d deine-domain.de auf. Mit Caddy als Webserver reicht es, den Domainnamen in die Konfiguration zu schreiben. Voraussetzung ist in beiden Fällen, dass die Domain per DNS auf deinen Server zeigt und Port 80 erreichbar ist.
Ist ein SSL-Zertifikat von Let’s Encrypt wirklich kostenlos?
Ja, ohne versteckte Kosten. Let’s Encrypt ist eine gemeinnützige Zertifizierungsstelle, die von Spenden und Sponsoren getragen wird. Die Verschlüsselung ist genauso stark wie bei einem gekauften Zertifikat. Bezahlt wird bei kommerziellen Anbietern vor allem für die Prüfung der Organisation, Support und Garantien.
Wie lange ist ein Let’s-Encrypt-Zertifikat gültig?
Aktuell 90 Tage. Ab dem 10. Februar 2027 stellt Let’s Encrypt standardmäßig 64-Tage-Zertifikate aus, bis Februar 2028 sinkt die Laufzeit auf 45 Tage. Mit automatischer Erneuerung durch certbot oder Caddy musst du dafür nichts ändern.
Wie erneuere ich ein SSL-Zertifikat?
Bei certbot passiert das automatisch über einen systemd-Timer, der zweimal täglich certbot renew aufruft. Von Hand testen kannst du mit sudo certbot renew --dry-run. Wichtig ist, dass der Webserver nach der Erneuerung neu geladen wird. Bei --nginx macht certbot das selbst, bei certonly brauchst du --deploy-hook "systemctl reload nginx".
Kann ich ein SSL-Zertifikat für eine IP-Adresse erstellen?
Lange ging das nur bei kommerziellen Anbietern. Let’s Encrypt bietet inzwischen kurzlebige Zertifikate (6 Tage) für IP-Adressen über ein eigenes Profil an. Dafür brauchst du einen aktuellen ACME-Client, der Profile unterstützt; das certbot 2.9 aus Ubuntu 24.04 gehört nicht dazu. Das haben wir nicht selbst getestet. Für eine normale Website ist ein Domainname aber fast immer die bessere Lösung.
Wie erstelle ich ein Wildcard-Zertifikat?
Nur über die DNS-Prüfung. Mit certbot und einem DNS-Plugin deines Anbieters, zum Beispiel certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/cloudflare.ini -d example.com -d '*.example.com'. Die Hauptdomain musst du zusätzlich angeben, weil *.example.com sie nicht abdeckt.
Was ist der Unterschied zwischen einem SSL- und einem TLS-Zertifikat?
Es gibt keinen. SSL ist der alte Name des Protokolls, TLS der aktuelle. Das Zertifikat ist in beiden Fällen dasselbe X.509-Zertifikat. „SSL-Zertifikat“ hat sich als Begriff einfach gehalten.
Wie erstelle ich ein selbstsigniertes Zertifikat?
Mit openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout key.pem -out cert.pem -days 365 -subj "/CN=intern.local" -addext "subjectAltName=DNS:intern.local". Ohne die SAN-Angabe akzeptieren moderne Browser das Zertifikat nicht. Für öffentliche Websites ist ein selbstsigniertes Zertifikat ungeeignet, weil jeder Besucher eine Warnung sieht.
Warum zeigt der Browser trotz Zertifikat „nicht sicher“?
Die häufigsten Gründe: Das Zertifikat ist abgelaufen, der aufgerufene Name (oft www.) steht nicht im Zertifikat, die Seite lädt Bilder oder Skripte über http:// (gemischte Inhalte), oder das Zertifikat ist selbstsigniert. Der Browser zeigt die genaue Ursache, wenn du auf das Symbol links neben der Adresse klickst.
Kann ich ein SSL-Zertifikat beim Webhoster erstellen?
Bei fast allen Hostern ja, meist mit einem Klick im Kundenbereich. Im Hintergrund läuft dort ebenfalls Let’s Encrypt. Diese Anleitung ist vor allem für eigene Server und VPS gedacht. Wenn du noch überlegst, ob sich ein eigener Server lohnt, hilft unser Artikel Was ist ein VPS?.
Muss ich nach einem Serverumzug ein neues Zertifikat erstellen?
Am sichersten ja. Stell den DNS-Eintrag um und lass certbot oder Caddy auf dem neuen Server ein neues Zertifikat holen. Wenn du das alte mitnehmen willst, kopiere das komplette Verzeichnis /etc/letsencrypt und prüf danach mit certbot certificates und certbot renew --dry-run. Nur die Zertifikatsdateien zu kopieren führt dazu, dass die Erneuerung still ausfällt.
Fazit: SSL-Zertifikat erstellen ist leicht, Erneuern ist die eigentliche Arbeit
Ein SSL-Zertifikat zu erstellen dauert mit certbot oder Caddy nur Sekunden und kostet nichts. Für neue Server empfehlen wir Caddy, weil es die Fehlerquellen rund um Erneuerung und Reload gar nicht erst entstehen lässt. Auf bestehenden nginx- oder Apache-Servern ist certbot mit dem passenden Plugin der richtige Weg. Ein Wildcard-Zertifikat holst du per DNS-Challenge, selbstsignierte Zertifikate sind nur für Tests und internes Netz gedacht.
Die eigentliche Arbeit kommt nach dem ersten grünen Schloss. Unsere Tests haben gezeigt, wo die Automatik leise versagt: ein Reload, der nie kommt, ein Port, der inzwischen belegt ist, und ein Umzug, nach dem certbot „alles gut“ meldet, weil es das Zertifikat gar nicht mehr kennt. Dagegen helfen drei Gewohnheiten: nach jeder Änderung certbot renew --dry-run, bei certonly immer ein Deploy-Hook und ein Ablauf-Check von außen, der nicht certbot fragt, sondern den Server. Mit 64-Tage-Zertifikaten ab Februar 2027 werden diese Gewohnheiten wichtiger, nicht weniger wichtig.
