Let's Encrypt & Certbot: HTTPS richtig einrichten

Kostenloses SSL/TLS mit Let's Encrypt und Certbot einrichten. Schritt-für-Schritt-Anleitung für Wildcard-Zertifikate, Auto-Renewal und HSTS.

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.