Pourquoi gérer Let's Encrypt soi-même sur un VPS
Let's Encrypt est une autorité de certification gratuite qui émet des certificats TLS validés par challenge automatisé (HTTP-01 ou DNS-01). Sur un hébergement mutualisé, vous subissez la config TLS du fournisseur : versions de protocole imposées, pas d'OCSP stapling, renouvellement opaque. Sur un VPS, vous tenez le certbot (ou acme.sh), le reverse proxy et la terminaison TLS. Vous décidez des protocoles (TLS 1.2/1.3 uniquement), des suites de chiffrement, du HSTS et de l'agrafage OCSP. Vous pouvez aussi émettre un certificat wildcard (*.votre-domaine.com) via le challenge DNS-01, couvrir plusieurs domaines dans un seul certificat (SAN), et brancher le renouvellement sur vos propres alertes. Bref : un contrôle total sur la sécurité de transport, condition d'un score A+ sur SSL Labs et d'une note de confiance maximale côté navigateur.
Ce que vous gagnez en self-hébergeant la chaîne TLS
- Certificats 100% gratuits, valables 90 jours et renouvelés automatiquement sans intervention.
- Wildcard
*.votre-domaine.compossible via le challenge DNS-01 : un seul certificat pour tous vos sous-domaines. - Maîtrise complète de la config : TLS 1.3, suites de chiffrement modernes, HSTS et OCSP stapling.
- Certificats SAN multi-domaines (
votre-domaine.com,www.votre-domaine.com,api.votre-domaine.com) sur une même IP. - Renouvellement scriptable et observable : hooks de reload, alertes Slack/e-mail avant expiration.
- Aucune dépendance à un panneau tiers : la même recette fonctionne sur tous vos VPS et environnements.
Prérequis avant de commencer
La terminaison TLS consomme très peu de ressources : un VPS 1 vCPU / 512 Mo à 1 Go de RAM suffit largement pour servir le HTTPS d'un ou plusieurs sites.
Côté réseau : votre nom de domaine doit pointer vers l'IP publique du VPS (enregistrement A/AAAA propagé). Vérifiez la propagation avant de lancer certbot : dig +short votre-domaine.com doit renvoyer l'IP de votre VPS. Un challenge qui échoue pour propagation DNS non terminée compte comme tentative et vous rapproche des rate-limits ACME.
Ports 80 et 443 ouverts dans le pare-feu — le port 80 est indispensable pour le challenge HTTP-01 (certbot y dépose un fichier de défi temporaire) :
ufw allow 80/tcp
ufw allow 443/tcp
ufw reloadPour un certificat wildcard (*.votre-domaine.com), le challenge HTTP-01 ne suffit pas : prévoyez un token API auprès de votre fournisseur DNS (Cloudflare, OVH…) afin d'automatiser le challenge DNS-01.
Installer certbot et obtenir un certificat Nginx
Installer certbot et le plugin Nginx
Sur Debian/Ubuntu :
apt update apt install -y certbot python3-certbot-nginxLe plugin
python3-certbot-nginxlit votre config Nginx, y injecte la configuration TLS et remplace la directivelisten 80par un bloc443 ssl. Si vous préférez garder la main sur Nginx, utilisez--nginx(automatique) ou--standalone/--webroot(manuel).Pointer le domaine et ouvrir les ports
Créez un enregistrement A
votre-domaine.com(et AAAA si IPv6) vers l'IP du VPS, puis autorisez le trafic web :ufw allow 80,443/tcpConfirmez la résolution avec
dig +short votre-domaine.comavant d'aller plus loin — un challenge échoue toujours si le DNS ne pointe pas encore.Émettre le certificat avec le plugin Nginx
La méthode la plus directe — certbot modifie votre vhost Nginx et gère le renouvellement :
certbot --nginx -d votre-domaine.com -d www.votre-domaine.comCertbot vous demande un e-mail (alertes d'expiration) et vous invite à accepter les CGU. Il émet le certificat, modifie
/etc/nginx/sites-available/<vhost>, ajoute un redirect HTTP→HTTPS et recharge Nginx sans coupure.Les fichiers sont déposés dans
/etc/letsencrypt/live/votre-domaine.com/:fullchain.pem(certificat + chaîne) etprivkey.pem(clé privée).Vérifier le renouvellement automatique
Certbot installe un timer systemd qui tente le renouvellement deux fois par jour :
systemctl status certbot.timerPour simuler un renouvellement sans émettre de nouveau certificat :
certbot renew --dry-runSi la simulation réussit, votre chaîne de renouvellement est opérationnelle. Assurez-vous que Nginx se recharge après un renouvellement effectif — certbot ajoute automatiquement un hook
--deploy-hook "systemctl reload nginx"dans/etc/letsencrypt/renewal/<domaine>.confavec le plugin Nginx.
Certificat wildcard avec DNS challenge Cloudflare
Un certificat *.votre-domaine.com couvre tous vos sous-domaines avec un seul fichier. Il est obligatoire si vous créez des sous-domaines dynamiquement ou si vous ne voulez pas émettre un certificat par sous-domaine.
Le challenge HTTP-01 ne peut pas valider un wildcard — il faut le challenge DNS-01, qui demande à certbot de créer temporairement un enregistrement TXT _acme-challenge.votre-domaine.com.
Avec Cloudflare (API token) :
apt install -y python3-certbot-dns-cloudflareCréez un fichier de credentials Cloudflare (permissions : 600) :
mkdir -p /etc/letsencrypt/cloudflare
chmod 700 /etc/letsencrypt/cloudflare
cat > /etc/letsencrypt/cloudflare/credentials.ini << 'EOF'
dns_cloudflare_api_token = VOTRE_TOKEN_API_CLOUDFLARE
EOF
chmod 600 /etc/letsencrypt/cloudflare/credentials.iniÉmettez le certificat wildcard :
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
-d votre-domaine.com \
-d '*.votre-domaine.com'Certbot crée l'enregistrement TXT, attend la propagation (par défaut 10 secondes — augmentable avec --dns-cloudflare-propagation-seconds 30), obtient le certificat, puis supprime l'enregistrement TXT.
Alternative avec acme.sh :
acme.sh --issue --dns dns_cf -d votre-domaine.com -d '*.votre-domaine.com'acme.sh utilise la variable d'environnement CF_Token ou CF_Key + CF_Email selon votre mode d'authentification Cloudflare.
Hardening TLS Nginx : TLS 1.2/1.3, HSTS et cipher suites
Le plugin certbot configure un HTTPS fonctionnel, mais la configuration par défaut n'est pas optimale pour un score A+ SSL Labs. Voici les directives à ajouter dans votre bloc server (ou dans un fichier inclus comme /etc/nginx/snippets/ssl-params.conf) :
# Protocoles : TLS 1.2 minimum, TLS 1.3 recommandé
ssl_protocols TLSv1.2 TLSv1.3;
# Suites de chiffrement modernes (Mozilla Intermediate)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# Session TLS
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
# HSTS (6 mois, sous-domaines inclus, preload)
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
# En-têtes de sécurité complémentaires
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;Redirigez tout le port 80 vers 443 dans un bloc server séparé :
server {
listen 80;
server_name votre-domaine.com www.votre-domaine.com;
return 301 https://$host$request_uri;
}Rechargez sans coupure : nginx -s reload. Testez ensuite sur SSL Labs — vous devriez obtenir A+.
Surveiller l'expiration des certificats
Le renouvellement automatique peut échouer silencieusement : timer systemd désactivé, port 80 bloqué après une modification du pare-feu, token API Cloudflare révoqué… Un certificat expiré en production interrompt le service pour tous vos visiteurs.
Vérification manuelle :
echo | openssl s_client -connect votre-domaine.com:443 -servername votre-domaine.com 2>/dev/null \
| openssl x509 -noout -datesScript d'alerte par e-mail (à placer dans cron ou systemd) :
#!/bin/bash
# Alerte si le certificat expire dans moins de 14 jours
cert_file="/etc/letsencrypt/live/votre-domaine.com/fullchain.pem"
if ! openssl x509 -checkend 1209600 -noout -in "$cert_file" 2>/dev/null; then
echo "ALERTE : certificat votre-domaine.com expire dans moins de 14 jours" \
| mail -s "[SSL] Expiration imminente" [email protected]
fi-checkend 1209600 correspond à 14 jours en secondes (14 × 86 400).
Avec Uptime Kuma : ajoutez un moniteur de type « Certificate Expiry » qui alerte à J-14 — sans script à maintenir.
Dépannage : rate-limits, port 80 bloqué, propagation DNS
Rate-limits ACME « too many certificates already issued »
Let's Encrypt limite à 5 certificats par domaine registrable et par semaine (enregistrement de type .com). Si vous atteignez cette limite en testant, basculez sur l'environnement de staging :
certbot --nginx --staging -d votre-domaine.comLe staging délivre des certificats non reconnus par les navigateurs, mais sans limite de taux. Une fois votre configuration validée, supprimez le certificat de staging et rééemettez en production.
Port 80 bloqué
Le challenge HTTP-01 exige que Let's Encrypt atteigne http://votre-domaine.com/.well-known/acme-challenge/. Vérifiez :
curl -I http://votre-domaine.com/.well-known/acme-challenge/test
# attendu : 404 (la route existe, le fichier n'existe pas encore)
ufw status | grep 80Si le port 80 est bloqué par un pare-feu amont (fournisseur cloud, Cloudflare en mode proxy), temporairement ouvrez-le ou basculez en challenge DNS-01.
Cloudflare proxy actif
Si votre domaine est derrière Cloudflare (icône orange), assurez-vous que le mode SSL de Cloudflare est Full (Strict) — en mode Flexible, Cloudflare communique avec votre serveur en HTTP et nginx qui redirige HTTP→HTTPS produit une boucle infinie (HTTP 525/526).
Propagation DNS insuffisante
Pour le challenge DNS-01, attendez au moins 60 secondes après la création du TXT avant de relancer certbot. Vérifiez :
dig TXT _acme-challenge.votre-domaine.com +shortSi la valeur n'apparaît pas encore, patientez avant de retenter.
Comparatif : certbot vs acme.sh vs Caddy HTTPS automatique
Faites défiler le tableau
| Critère | certbot | acme.sh | Caddy (HTTPS auto) |
|---|---|---|---|
| Installation | `apt install certbot` | Script bash, aucune dépendance système | Binaire unique, inclus |
| Intégration Nginx/Apache | Plugin natif (`--nginx`, `--apache`) | Hooks `--install-cert` manuels | Gère son propre reverse proxy |
| Wildcard DNS-01 | Via plugin DNS (`python3-certbot-dns-*`) | Natif, nombreux providers DNS intégrés | Via module `tls.dns.*` (Caddy v2) |
| Renouvellement | Timer systemd (automatique) | Cronjob généré à l'installation | Natif, sans configuration |
| Courbe d'apprentissage | Faible — commandes intuitives | Moyenne — nombreuses options | Très faible — `Caddyfile` minimaliste |
| Cas d'usage idéal | Serveur dédié Nginx/Apache existant | Environnements complexes, multi-providers DNS | Nouveaux projets, reverse proxy intégré |
Tester avant la production
Avant de mettre en production, testez contre l'environnement de staging de Let's Encrypt (--staging chez Certbot, --server letsencrypt_test chez acme.sh) : la limite de production est de 5 émissions par domaine et par semaine, et un script mal réglé peut vite vous y faire buter.
Ajoutez aussi un certificat de monitoring d'expiration : une sonde qui exécute openssl x509 -checkend 604800 (7 jours) et déclenche une alerte si le renouvellement automatique a silencieusement échoué — un cron cassé est la cause numéro un d'un HTTPS expiré en pleine nuit.
Consultez la page de releases certbot sur GitHub pour vérifier la version disponible avant d'écrire un tutoriel ou un script d'installation.