Let's Encrypt & Certbot: HTTPS richtig einrichten
2015 hat Let's Encrypt das Web verändert. Vorher war HTTPS ein Feature, das man bei großen Hosts dazukaufte — heute ist HTTP eine Anomalie. Das ist eine der wenigen Erfolgsgeschichten der letzten zehn Jahre Web-Infrastruktur, die wirklich geliefert hat: kostenlose, automatisierte, vertrauenswürdige Zertifikate für jeden.
Ich nutze Let's Encrypt für alle meine Domains, und in den letzten Jahren ist die Setup-Komplexität von "halber Tag Tutorial" auf "drei Befehle" gefallen. Hier ist, was 2026 noch relevant ist.
Certbot ist Standard, aber nicht alternativlos
Certbot ist das EFF-eigene Tool und das, was die meisten Anleitungen verwenden. Es funktioniert, hat aber im Vergleich zu Alternativen wie acme.sh oder lego einen größeren Footprint. Für den Single-Domain-Fall: Certbot. Für komplexere Setups (DNS-API-Provider, viele Domains): einer der Alternativen.
Ich bin auf Certbot geblieben, weil es per apt installierbar ist und keine zusätzlichen Dependencies hat.
Standard-Setup mit nginx
apt install certbot python3-certbot-nginx
certbot --nginx -d matti.services
Das war's für den Standardfall. Certbot fragt nach Email, akzeptiert die ToS, schreibt die nginx-Config um, fügt eine systemd-Unit für die Erneuerung an. Bei Erstinstallation einmal certbot renew --dry-run laufen lassen, um sicherzugehen, dass der Renewal funktioniert.
Die generierten nginx-Blöcke sind brauchbar, aber Certbot fummelt direkt in den Config-Dateien — das mag ich nicht. Ich nutze certonly:
certbot certonly --nginx -d matti.services
Damit wird das Zertifikat ausgestellt, aber die nginx-Config nicht angefasst. Die Pfade trage ich manuell ein:
ssl_certificate /etc/letsencrypt/live/matti.services/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matti.services/privkey.pem;
Symlink-magic erledigt Certbot — beim Renewal verweisen die Pfade automatisch auf das neue Zertifikat.
Wildcard-Zertifikate
Für *.matti.services brauchst du DNS-01-Validierung statt HTTP-01. HTTP-01 funktioniert nicht für Wildcards, weil Let's Encrypt für jede Subdomain einzeln einen Challenge-File hosten müsste — was bei * nicht definiert ist.
DNS-01 erfordert einen API-fähigen DNS-Provider. Cloudflare, Hetzner, deSEC haben Plugins:
apt install python3-certbot-dns-cloudflare
Credentials in /root/.secrets/cloudflare.ini:
dns_cloudflare_api_token = ...
Permissions auf 600, sonst meckert Certbot:
chmod 600 /root/.secrets/cloudflare.ini
certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d matti.services -d "*.matti.services"
Nach 60 Sekunden Wartezeit für DNS-Propagation ist das Zertifikat da.
HSTS, aber bitte mit Verstand
HSTS sagt dem Browser: "Komm hier nie wieder per HTTP rein." Klingt gut, ist auch gut, aber ein einmal gesetzter HSTS-Header gilt für die spezifizierte Dauer — und der Browser cached das. Wer HSTS mit max-age=31536000; includeSubDomains; preload setzt und ein Subdomain-Setup nicht vollständig auf HTTPS hat, lockt sich für ein Jahr aus.
Mein Vorschlag: erst mit kurzem max-age testen.
add_header Strict-Transport-Security "max-age=300" always;
Wenn nach ein paar Tagen alles funktioniert, hochstufen:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
preload setzen nur, wenn man wirklich versteht, was es bedeutet. Die Liste auf hstspreload.org wird in Browsern hartkodiert ausgeliefert — Removal dauert Monate.
Auto-Renewal: prüfen, dass es läuft
Certbot installiert per Default einen systemd-Timer (certbot.timer), der zweimal am Tag läuft.
systemctl list-timers | grep certbot
Mein wichtigster Tipp: einmal im Quartal anschauen, dass Renewals tatsächlich erfolgreich waren. Logs in /var/log/letsencrypt/. Stille Fehlschläge passieren — etwa wenn DNS-Records sich geändert haben oder das webroot-Verzeichnis verschoben wurde.
Ich hatte einmal den Fall, dass ein Zertifikat 5 Tage abgelaufen war, weil eine nginx-Config nicht mehr zur Renewal-Methode passte. Seitdem läuft bei mir ein Cron, der mich täglich an die Restdauer aller Zertifikate erinnert:
for cert in /etc/letsencrypt/live/*/cert.pem; do
domain=$(basename $(dirname $cert))
expires=$(openssl x509 -in $cert -enddate -noout | cut -d= -f2)
echo "$domain: $expires"
done
In ein Cron-Script, gemailt, fertig.
Was sich geändert hat
ACME v2 ist Standard, ACMEv1 abgeschaltet. Validierung läuft jetzt mehrfach von verschiedenen Punkten ("Multi-Perspective Validation"), was BGP-Hijacking-Angriffe deutlich erschwert. Transparent, man muss nichts anpassen. Aber: wenn deine DNS-Konfiguration regional unterschiedliche Antworten liefert, kann das zu Renewal-Fehlern führen. Bei Geo-DNS-Setups schon vorgekommen, bei mir nie.