Installer Certbot pour nginx sur Ubuntu 24.04
Installez Certbot avec apt ou snap, lancez certbot --nginx et évitez les doublons de timer. Découvrez aussi le piège du renouvellement bloqué sur le port 80.
Installer Certbot : apt ou snap
Sur Ubuntu 24.04, sudo apt install certbot python3-certbot-nginx installe un Certbot fonctionnel qui émet de vrais certificats Let's Encrypt approuvés publiquement. La documentation amont de Certbot vous oriente plutôt vers le snap. La différence est limitée : le snap suit les versions amont, tandis que le paquet d’archive suit la version fournie avec la LTS et reçoit les correctifs de sécurité.
Choisissez une seule méthode. Deux installations de Certbot créent deux timers de renouvellement visant la même arborescence /etc/letsencrypt. Celle que vous avez oubliée est alors celle qui vous surprend.
Méthode apt :
sudo apt update
sudo apt install certbot python3-certbot-nginxCette commande installe /usr/bin/certbot, le plugin nginx, une paire certbot.service + certbot.timer et une entrée /etc/cron.d/certbot qui n’effectue aucune action sous systemd.
Méthode snap :
sudo apt remove certbot python3-certbot-nginx
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotLe snap fournit son propre timer, snap.certbot.renew.timer. Supprimez le paquet apt avant d’installer le snap.
Les deux installations fonctionnent ensuite de la même manière. Par défaut, Certbot 2.x utilise des clés ECDSA (P-256). Utilisez --key-type rsa uniquement pour un client qui ne prend pas en charge ECDSA. Tout l’état est stocké sous /etc/letsencrypt : archive/ contient les vrais fichiers de clé et de certificat, live/ pointe par des liens symboliques vers les fichiers actuels, renewal/ contient un fichier de configuration par certificat et accounts/ contient la clé de compte ACME.
Ce que fait réellement HTTP-01 et pourquoi le port 80 est obligatoire
Le challenge HTTP-01 est un callback. Vous demandez à Let's Encrypt un certificat couvrant example.com. Le serveur résout le nom dans le DNS public, ouvre une connexion sur le port 80 à l’adresse trouvée, puis demande http://example.com/.well-known/acme-challenge/<token>. Votre serveur répond avec le contenu exact du token que Certbot vient d’écrire sur le disque. C’est tout le mécanisme. Trois conséquences en découlent. Elles expliquent la plupart des échecs d’émission.
- Le port 80 doit être accessible depuis Internet, et pas uniquement depuis votre ordinateur portable. Une règle
ufw, un security group du fournisseur cloud ou un pare-feu de la console du VPS qui n’ouvre que le port 443 bloque l’émission ainsi que tous les renouvellements ultérieurs. - Le DNS doit déjà pointer vers ce serveur. Le serveur de validation effectue sa propre résolution depuis l’extérieur. Vos entrées
/etc/hostset le cache de votre navigateur n’ont aucun effet sur lui. - Si vous publiez un enregistrement AAAA, IPv6 est essayé en premier. Let's Encrypt réessaie via IPv4 si la connexion IPv6 échoue immédiatement. En revanche, un enregistrement AAAA obsolète qui pointe vers un hôte qui accepte la connexion et sert autre chose provoque un échec définitif.
Les redirections sont autorisées : la validation suit une redirection HTTP vers HTTPS et ne tient pas compte du fait que le certificat présenté par l’autre extrémité est absent, expiré ou autosigné. En revanche, elle ne commencera jamais ailleurs que sur le port 80. Certbot n’implémente pas TLS-ALPN-01. « Utiliser simplement 443 » n’est donc pas une solution de contournement.
Choisir un authenticator : --nginx, --webroot, --standalone
--nginx est le choix par défaut lorsque nginx est déjà en cours d’exécution et sert déjà le domaine. Certbot analyse votre configuration, ajoute temporairement un emplacement pour le challenge, recharge nginx, effectue la validation, puis écrit les directives TLS dans votre server block. Sans interruption de service.
sudo certbot --nginx -d example.com -d www.example.comPour un serveur fraîchement installé et une exécution automatisée :
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot convient lorsque vous ne voulez pas que Certbot intervienne dans la configuration nginx, par exemple si vous la générez à partir d’un template, la conservez dans git ou la déployez avec Ansible. Certbot écrit uniquement le fichier de challenge dans un répertoire que vous servez déjà.
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone convient lorsqu’aucun service n’écoute sur le port 80 : un serveur de messagerie, une API qui n’accepte que les connexions sur 443 ou un script de premier démarrage exécuté avant l’installation de nginx. Certbot se lie lui-même au port 80 pendant quelques secondes. Si nginx est en cours d’exécution, cette commande échoue. Arrêtez-le le temps de l’exécuter :
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Ces hooks sont enregistrés dans la configuration de renouvellement du certificat. Le même arrêt et redémarrage sont donc effectués automatiquement lors du renouvellement.
Un bloc server qui fonctionne avant et après la création du certificat
Le problème de dépendance circulaire est le suivant : nginx refuse de démarrer si ssl_certificate pointe vers un fichier inexistant, et Certbot ne peut pas effectuer la validation si nginx est arrêté. Commencez par rendre le site accessible sur le port 80.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
try_files $uri =404;
}
location / {
try_files $uri $uri/ =404;
}
}Exécutez sudo nginx -t && sudo systemctl reload nginx, vérifiez que curl -I http://example.com/ répond depuis l’extérieur du serveur, puis lancez l’émission du certificat. Ensuite :
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
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;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}Le préfixe ^~ sur l’emplacement ACME est indispensable : il empêche le bloc return 301 d’intercepter la requête de validation. Conserver cet emplacement sur le port 80 permet aux renouvellements de continuer à fonctionner après le passage du reste du site en HTTPS uniquement.
Les deux blocs ci-dessus servent des fichiers depuis le disque. Si nginx sert à la place de reverse proxy devant une application, location / devient un bloc proxy_pass et le bloc server de reverse proxy, ligne par ligne détaille les en-têtes nécessaires à cette application. L’emplacement ACME et les directives TLS restent exactement inchangés.
La syntaxe HTTP/2 dépend de votre version de nginx. Mélanger les deux formes provoque une erreur au démarrage. Ubuntu 24.04 fournit nginx 1.24, qui attend la syntaxe en ligne, listen 443 ssl http2;. Debian 13 fournit une version plus récente de nginx, qui attend la directive http2 on; séparée. Vérifiez d’abord nginx -v.
Faites pointer nginx vers live/, jamais vers archive/. Les liens symboliques live/ sont réorientés à chaque renouvellement. Un chemin fixe vers archive/ vous laisse utiliser un certificat qui finit par expirer.
Les wildcards impliquent DNS-01, et DNS-01 implique un plugin
Un certificat wildcard (*.example.com) ne peut pas être validé avec HTTP-01 : aucun nom d’hôte unique ne permet de récupérer un fichier. DNS-01 est la seule méthode possible : vous prouvez que vous contrôlez le domaine en publiant un enregistrement TXT _acme-challenge.example.com. Pour effectuer cette opération sans intervention, Certbot a besoin des identifiants API de votre fournisseur DNS. C’est précisément le rôle des plugins du fournisseur. Le guide complet sur les certificats wildcard explique le fonctionnement des enregistrements TXT et le piège du renouvellement en mode manuel ; la version courte pour Cloudflare suit.
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareAvec apt, le paquet est sudo apt install python3-certbot-dns-cloudflare à la place. Les identifiants doivent se trouver dans un fichier accessible uniquement à root :
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereLimitez le token aux droits de modification du DNS sur cette seule zone. Il donne accès à votre DNS ; traitez-le comme une clé cryptographique.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'Mettez le wildcard entre guillemets pour que votre shell ne l’interprète pas comme un motif glob. DNS-01 résout également les cas auxquels HTTP-01 ne peut pas répondre : les certificats pour des hôtes sans port 80 public, un service interne, une machine accessible uniquement via un VPN WireGuard auto-hébergé sur un VPS, ou un panneau d’administration lié à une interface privée.
Renouvellement : les 90 jours, le timer et le deploy hook
Les certificats Let’s Encrypt sont valides pendant 90 jours. Certbot les renouvelle lorsqu’il reste moins de 30 jours, ce qui vous laisse une fenêtre de 30 jours pour corriger un renouvellement défaillant avant qu’il ne provoque une interruption de service. Let’s Encrypt n’envoie plus d’e-mails d’avertissement avant expiration. Personne ne vous préviendra : la supervision vous incombe désormais.
Vérifiez le timer fourni par votre installation :
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew parcourt toutes les configurations dans /etc/letsencrypt/renewal/, ignore celles qui sont en dehors de la fenêtre de 30 jours et renouvelle les autres en utilisant exactement les options de l’exécution initiale. La première exécution est donc importante : c’est elle qui est enregistrée.
Renouveler le fichier sur le disque ne suffit pas. nginx continue de servir l’ancien certificat chargé en mémoire jusqu’à ce qu’une commande lui demande de recharger sa configuration. Configurez une seule fois un deploy hook :
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shTout fichier exécutable dans renewal-hooks/deploy/ est exécuté après chaque renouvellement réussi. L’option --deploy-hook fait la même chose pour un certificat donné et enregistre renew_hook = ... dans sa configuration de renouvellement. certbot --nginx recharge la configuration pour vous ; les installations --webroot et --standalone ne le font pas. L’absence de hook explique exactement pourquoi un site peut servir un certificat expiré alors que certbot certificates signale correctement qu’un nouveau certificat est disponible. Tout autre service qui lit le certificat au démarrage nécessite le même hook. Une application conteneurisée, comme une installation de Nextcloud VPS avec Docker, TLS et sauvegardes, doit également avoir sa propre étape de redémarrage ou de rechargement configurée ici.
Test réel du renouvellement
sudo certbot renew --dry-runCette commande exécute le challenge complet auprès de l’environnement de staging de Let’s Encrypt : même chemin de code, même pare-feu, même DNS, sans consommer de quota de limitation de débit et sans rien écrire sur le disque. Si elle réussit aujourd’hui, le renouvellement automatique dans 60 jours réussira également, à condition que rien ne change entre-temps sur le serveur.
Un test à blanc ne prouve pas que votre hook de rechargement s’exécute ; son comportement varie selon la version de Certbot. Testez cette partie manuellement : exécutez directement le script du hook, vérifiez que systemctl reload nginx réussit, puis contrôlez sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.
Les erreurs que vous rencontrerez réellement
Could not bind to IPv4 or IPv6., --standalone alors que nginx utilise déjà le port 80. Utilisez --nginx ou --webroot, ou arrêtez nginx le temps de l’exécution. Vérifiez quel processus utilise le port avec sudo ss -lntp | grep ':80'.
Timeout during connect (likely firewall problem), Let’s Encrypt n’a pas pu joindre le port 80. Vérifiez chaque élément en partant de l’extérieur : sudo ufw status (ouvrez-le avec sudo ufw allow 'Nginx Full'), puis le pare-feu du fournisseur VPS, puis le DNS. Testez depuis une machine qui n’est pas votre serveur : curl -sSv http://example.com/.well-known/acme-challenge/test. Un enregistrement AAAA obsolète produit le même message.
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404, le port 80 est accessible, mais le token n’est pas servi. La requête est arrivée dans un autre server block (vérifiez lequel possède default_server), ou le répertoire transmis à -w n’est pas celui servi par nginx. Créez un fichier dans /var/www/example.com/.well-known/acme-challenge/test et récupérez-le depuis l’extérieur ; si la requête renvoie une erreur 404, le certificat n’était pas en cause.
DNS problem: NXDOMAIN looking up A for example.com, le nom ne se résout pas publiquement. Il peut s’agir de nouveaux enregistrements qui ne sont pas encore propagés, ou d’un enregistrement situé dans une zone que votre registrar ne sert pas.
too many certificates already issued for: example.com, une limite de débit, que l’on atteint souvent en boucle pendant le dépannage. Let’s Encrypt limite à cinq par semaine les certificats en double, c’est-à-dire avec exactement le même ensemble de noms. Une autre limite autorise séparément 50 nouveaux certificats par domaine enregistré et par semaine. Seul le temps permet de dépasser ces limites. Effectuez les tests avec l’environnement staging à l’aide de --dry-run.
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory, nginx est configuré pour un certificat qui n’a jamais été émis, ou qui a été supprimé avec certbot delete. Commentez le server block TLS, démarrez nginx, émettez le certificat, puis restaurez le block.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed, ce fichier est fourni avec le paquet du plugin nginx. Sur une machine certonly sans python3-certbot-nginx, ajoutez le plugin ou remplacez la ligne include par vos propres paramètres ssl_protocols et ssl_ciphers.
Gérer cela à grande échelle
Un certificat peut contenir jusqu’à 100 noms, et un seul certbot --nginx -d a.example.com -d b.example.com ... peut sembler pratique. Mais un ancien enregistrement DNS qui échoue à la validation peut alors invalider tous les autres noms de ce certificat. Des certificats distincts pour chaque site échouent indépendamment. C’est ce qu’il faut sur un serveur qui héberge plus de quelques services. Au-delà de quelques sites, un point d’entrée compatible avec ACME devient utile : un proxy inverse Traefik exécutant plusieurs applications avec Docker Compose demande et renouvelle lui-même les certificats, et Certbot disparaît complètement de l’architecture. Le choix du proxy à placer à ce point d’entrée est une décision distincte. Comparer Nginx, Caddy et Traefik revient surtout à déterminer quelle quantité de gestion des certificats et de configuration par application vous voulez déléguer au proxy.
Sauvegardez /etc/letsencrypt dans son intégralité, avec sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt et les liens symboliques. Cette arborescence contient accounts/, votre clé de compte ACME, que vous ne pouvez pas régénérer à l’identique. La migration vers un nouveau VPS se résume alors à synchroniser l’arborescence avec -a, installer Certbot, modifier le DNS et exécuter certbot renew --dry-run avant la bascule.
Si vous reconstruisez le serveur ou migrez vers une nouvelle version LTS, le timer de renouvellement ne vous suit pas. Après toute migration, restauration d’un snapshot ou mise à niveau de la distribution, exécutez systemctl list-timers 'certbot*' et un --dry-run. Si vous ignorez cette étape, un site peut devenir inaccessible 89 jours plus tard, à 3 heures du matin, avec un certificat dont tout le monde pensait qu’il se renouvelait automatiquement.
Tout cela suppose une machine que vous contrôlez, avec une IP publique et le port 80 ouvert sur Internet : un VPS, autrement dit. Les étapes décrites ci-dessus sont identiques sur toutes ces machines.
Les mêmes étapes s’appliquent avec Apache au lieu de nginx. Lorsqu’un certificat public n’est pas possible, un certificat auto-signé sur Ubuntu convient aux services internes.
FAQ
Dois-je ouvrir le port 80 si mon site sert uniquement du HTTPS ?
Oui, pour le challenge HTTP-01. Let’s Encrypt commence toujours sa requête de validation sur le port 80, et Certbot n’implémente pas TLS-ALPN-01. Un pare-feu qui n’ouvre que le port 443 bloque donc la première émission et chaque renouvellement automatique suivant. Une redirection du port 80 vers HTTPS convient : la validation la suit. Le seul moyen de ne pas ouvrir du tout le port 80 est d’utiliser DNS-01 avec un plugin de fournisseur.
apt ou snap : quelle version de Certbot dois-je installer pour nginx sur Ubuntu 24.04 ?
Utilisez apt. sudo apt install certbot python3-certbot-nginx fournit Certbot 2.9.0 sur Ubuntu 24.04. Cette version est suffisamment récente pour tout le contenu de ce guide, reçoit les correctifs de sécurité via unattended-upgrades et ne nécessite pas snapd. Choisissez snap uniquement si vous avez besoin immédiatement de la version la plus récente ou d’un plugin DNS distribué exclusivement sous forme de snap. Dans tous les cas, choisissez une seule méthode : deux installations créent deux timers de renouvellement qui ciblent le même arbre /etc/letsencrypt, et c’est celle que vous oubliez qui finit par poser problème.
Certbot peut-il émettre un certificat wildcard pour nginx ?
Uniquement avec DNS-01. Un wildcard comme *.example.com ne correspond pas à un nom d’hôte unique depuis lequel récupérer un fichier de challenge. --nginx, --webroot et --standalone sont donc tous exclus. Installez le plugin de votre fournisseur DNS, placez un token d’API limité dans un fichier d’identifiants accessible uniquement par root, puis exécutez certbot certonly --dns-cloudflare -d example.com -d '*.example.com' en plaçant le wildcard entre guillemets pour empêcher le shell de l’interpréter comme un motif global.
Pourquoi nginx sert-il encore l’ancien certificat après un renouvellement réussi ?
nginx conserve le certificat en mémoire et ne détecte pas le nouveau fichier sur le disque tant qu’il n’est pas rechargé. certbot --nginx effectue le rechargement automatiquement, mais --webroot et --standalone ne le font pas. Le renouvellement peut donc réussir alors que le navigateur affiche encore un certificat arrivant à expiration. Ajoutez un script exécutable dans /etc/letsencrypt/renewal-hooks/deploy/ qui exécute nginx -t && systemctl reload nginx. Il sera lancé après chaque renouvellement réussi.
certbot renew --dry-run prouve-t-il que le renouvellement fonctionnera ?
En grande partie. Cette commande exécute le véritable challenge dans l’environnement de staging, avec le même pare-feu, le même DNS et le même chemin d’exécution, sans consommer de quota et sans écrire sur le disque. Une réussite confirme donc que la partie réseau fonctionne. Elle ne prouve toutefois pas de manière fiable que votre deploy hook sera exécuté. Testez-le séparément : exécutez le script du hook manuellement et vérifiez sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.