Servir HTTPS avec Nginx tient en quelques lignes ; le difficile est de renouveler le certificat sans coupure et de prouver que le nouveau est bien celui que les clients reçoivent. Cette leçon s'adresse à une personne qui publie déjà une application derrière Nginx. Elle enseigne un bloc server TLS minimal, le choix d'une voie d'automatisation stable (un client ACME externe, programme qui obtient le certificat auprès de l'autorité) plutôt que d'un module encore en aperçu, le passage du défi HTTP-01 (l'épreuve par laquelle l'autorité vérifie que vous contrôlez le nom) à travers une redirection, le renouvellement par un reload, puis la vérification de la série (le numéro de série du certificat) réellement servie. Les certificats de 45 jours rendent ce cycle indispensable.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »La leçon suit le cycle de vie d'un certificat : servir, obtenir, renouveler, vérifier.
- Écrire un
serverTLS minimal avec la chaîne complète et la clé. - Choisir une voie d'automatisation adaptée à la version de Nginx installée.
- Laisser passer le défi HTTP-01 en clair, malgré la redirection vers HTTPS.
- Renouveler par un
reload, sans redémarrage et sans nouveau compte ACME. - Vérifier nom, chaîne et série servie avec un client qui valide.
Prérequis
Section intitulée « Prérequis »La leçon suppose que vous comprenez le principe d'ACME et que vous savez publier une application derrière Nginx.
- Le protocole ACME, les défis et les certificats de 45 jours : Certificats automatiques : ACME, défis HTTP-01, TLS-ALPN-01 et DNS-01.
- Publier une application avec
proxy_pass: Publier une application avec Nginx. - Valider et recharger sans couper : Installer Nginx.
- Un compte avec
sudosur une machine Debian 13 où Docker est installé.
Comment Nginx sert-il HTTPS ?
Section intitulée « Comment Nginx sert-il HTTPS ? »Un server qui écoute avec listen 443 ssl doit recevoir deux fichiers : la chaîne de certificats (ssl_certificate) et la clé privée (ssl_certificate_key). Le certificat associe un nom à une clé publique, signé par une autorité de certification (l'organisme qui garantit ce lien). La chaîne complète (souvent appelée fullchain) contient ce certificat suivi des certificats intermédiaires : un client qui ne reçoit pas les intermédiaires ne peut pas remonter jusqu'à une racine qu'il connaît.
server { listen 443 ssl; server_name app.lab.test api.lab.test;
ssl_certificate /etc/nginx/tls/app.fullchain.pem; ssl_certificate_key /etc/nginx/tls/app.key; ssl_protocols TLSv1.2 TLSv1.3;
location / { proxy_pass http://127.0.0.1:18097; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }}Deux détails comptent. Le certificat doit porter tous les noms servis dans ses SAN (Subject Alternative Names, la liste des noms que le certificat couvre) : un certificat émis pour app.lab.test seul fait échouer la validation sur api.lab.test. Et ssl_protocols écarte les versions anciennes : TLS 1.2 et 1.3 suffisent aujourd'hui.
Sur le choix du certificat, rappelez-vous que Nginx le choisit d'après le nom demandé par le client (SNI, Server Name Indication) : plusieurs server TLS sur la même adresse coexistent, chacun avec son certificat. Le trajet complet est décrit dans Publier un service web ; ici, un seul certificat couvre deux noms.
Quelle voie choisir pour obtenir et renouveler le certificat ?
Section intitulée « Quelle voie choisir pour obtenir et renouveler le certificat ? »La voie principale de ce module est un client ACME externe : stable, indépendant de la version de Nginx, et rejouable en lab. Le client parle ACME à l'autorité, dépose les fichiers sur le disque, puis Nginx les relit par un rechargement. Dans le lab, ce client est lego, fourni en image de conteneur, face à une autorité locale (Pebble), ce qui évite tout quota et toute dépendance à Internet.
Trois voies existent, et leur statut diffère.
| Voie | Statut | Ce qu'elle suppose |
|---|---|---|
| Client externe (lego, acme.sh) | Stable | Un script qui renouvelle puis recharge Nginx |
| certbot | Stable, très répandu | Le paquet certbot ; son greffon Nginx modifie la configuration |
Module natif ngx_http_acme_module | Preview | Un module dynamique à installer, HTTP-01 et TLS-ALPN-01 (pas de DNS-01) |
Le tableau se lit de haut en bas comme un ordre de prudence. Rappel sur les défis : HTTP-01 dépose un jeton sur le port 80, TLS-ALPN-01 joue l'épreuve dans la négociation TLS sur le port 443 (grâce à l'extension ALPN, qui sert à choisir le protocole), DNS-01 publie un enregistrement DNS et seul il permet les certificats jokers ; la leçon Certificats automatiques les compare. Le module natif a l'avantage de garder l'état dans Nginx, mais il est en aperçu (preview) : son interface peut encore changer (ses versions sont en 0.x, la 0.4.1 datant de mai 2026), il ne gère que les défis HTTP-01 et TLS-ALPN-01 (ce dernier depuis la 0.2.0), jamais DNS-01, et sa disponibilité dépend de la version et du paquet. Il ne doit jamais devenir une dépendance implicite de votre procédure. certbot reste le plan B éprouvé quand un client générique ne convient pas.
Le lab de cette leçon n'utilise pas le module natif : il note un résultat (la nouvelle série servie), pas un outil. La page Publier une application avec Nginx présente la configuration du module pour qui veut l'essayer ; gardez en tête son statut de preview.
Comment le défi HTTP-01 traverse-t-il une redirection vers HTTPS ?
Section intitulée « Comment le défi HTTP-01 traverse-t-il une redirection vers HTTPS ? »Le défi HTTP-01 se joue en clair sur le port 80 : l'autorité va chercher un jeton à l'adresse /.well-known/acme-challenge/, et la bonne pratique est de servir ce chemin en clair, sans le soumettre à la redirection vers HTTPS. Une redirection globale (return 301 https://$host$request_uri;) n'est pas interdite en elle-même : Let's Encrypt suit les redirections, dix au plus, uniquement vers les ports 80 ou 443, sans vérifier le certificat d'une cible HTTPS. Mais trois raisons justifient de ne pas s'y fier, et la première ne dépend d'aucune autorité.
- La première émission ne peut pas passer par là. Au moment du premier défi, aucun certificat n'existe : le serveur TLS n'écoute pas encore sur le port 443, et la redirection envoie l'autorité vers un port fermé. Ce motif est structurel, il ne dépend ni de l'autorité ni du client.
- Le défi dépend de ce que l'autorité accepte de suivre. Let's Encrypt suit les redirections, mais une autre autorité peut ne pas les suivre, ou les suivre selon d'autres limites.
- La cible doit rester dans les ports autorisés, 80 et 443, et servir elle-même le jeton : le
serverHTTPS de la leçon envoie tout à l'application, qui ne connaît pas ce jeton.
Le risque le plus coûteux touche le renouvellement : s'il échoue, il le fait en silence, des semaines après la première émission, quand plus personne ne regarde. Ne supprimez pas pour autant la redirection : elle continue d'envoyer les visiteurs vers HTTPS. La parade est de réserver un location pour le défi, servi en clair depuis un répertoire que le client remplit, et de rediriger tout le reste. C'est la seule partie qui ne dépende que de votre propre configuration.
server { listen 80; server_name app.lab.test api.lab.test;
location /.well-known/acme-challenge/ { root /var/www/acme; }
location / { return 301 https://$host$request_uri; }}Le location le plus spécifique l'emporte sur location / : une requête sur le chemin du défi reçoit le fichier, toute autre reçoit la redirection 301. Le répertoire root est celui où le client ACME écrit ses jetons ; Nginx n'a qu'à les servir.
Le schéma suivant montre l'ensemble du renouvellement, défi compris. Suivez les flèches de haut en bas : le point d'entrée lance le client, l'autorité vient chercher le jeton sur le port 80, puis le certificat est écrit sur le disque et Nginx est rechargé.
Comment renouveler sans redémarrer ni perdre le compte ACME ?
Section intitulée « Comment renouveler sans redémarrer ni perdre le compte ACME ? »Un renouvellement se met en service par un reload, qui conserve les connexions en cours et le même processus maître (même PID, l'identifiant du processus), et il réutilise le compte ACME déjà enregistré. Le client ACME écrit le nouveau certificat sur le disque, mais Nginx garde l'ancien en mémoire tant qu'il n'a pas relu ses fichiers : sans reload, le renouvellement a eu lieu et rien n'a changé pour les visiteurs. Un restart marcherait aussi, mais il coupe les connexions en cours, ce que le reload évite.
Le compte ACME est l'identité (une paire de clés) que le client enregistre auprès de l'autorité à la première émission. Le réutiliser est la règle : effacer le compte pour repartir de zéro fonctionne en lab, et c'est exactement ce qu'il ne faut pas faire. Face à une autorité réelle, chaque inscription consomme un quota et fait perdre l'historique des émissions.
Le geste se range dans un point d'entrée unique, un script exécutable qui enchaîne les trois étapes. Le squelette suivant montre l'enchaînement ; la ligne de commande exacte du client dépend de l'outil et de sa version, et vous l'écrivez à partir de sa documentation.
#!/usr/bin/env bashset -euo pipefail
# 1. Renouveler avec le compte existant : commande de votre client ACME.# (Si rien n'est à renouveler, le client le dit et ne fait rien.)
# 2. Valider avant de recharger : une erreur ici n'interrompt pas le service.nginx -t
# 3. Mettre en service par un reload, jamais par un restart.systemctl reload nginxUn script ne se déclenche pas tout seul. Le mécanisme est libre : une minuterie systemd, une tâche cron ou un hook du client. La minuterie a l'avantage d'une journalisation par journalctl et d'un décalage aléatoire qui évite que tous les serveurs frappent l'autorité au même instant.
[Timer]OnCalendar=dailyRandomizedDelaySec=1hPersistent=true
[Install]WantedBy=timers.targetCette minuterie, reliée à un service qui exécute le script, le déclenche chaque jour ; le script ne renouvelle que si le certificat approche de son terme. Avec des certificats de 45 jours, Let's Encrypt recommande de renouveler aux deux tiers de la durée de vie, soit autour du trentième jour, comme l'explique la leçon sur ACME. Une exécution quotidienne laisse donc une marge de quinze jours pour absorber une panne d'autorité ou une erreur de configuration.
Comment protéger la clé privée et que faire avec HSTS ?
Section intitulée « Comment protéger la clé privée et que faire avec HSTS ? »La clé privée ne doit être lisible que par ceux qui en ont besoin : qui la lit peut se faire passer pour votre service. Nginx lit la clé avec les droits de root au démarrage et au rechargement, puis ses processus de travail (workers), qui tournent sans ces droits, n'en ont plus besoin : la clé n'a donc pas à être lisible par tous. Appliquez la règle simple : propriétaire root, mode 0600 pour la clé et pour le fichier de compte du client, mode 0700 pour leur répertoire.
sudo find /chemin/du/client -type f \( -name '*.key' -o -name '*.json' \) -perm -o=rCette commande liste les fichiers de clé ou de compte lisibles par tout le monde ; une sortie vide est le résultat attendu. Faites-la après chaque première émission : un client lancé dans un conteneur crée parfois ses fichiers avec des droits plus larges que prévu. Fermer les droits ne doit pas casser le service, et c'est pourquoi il faut aussi vérifier que HTTPS répond encore.
L'en-tête HSTS (Strict-Transport-Security) demande au navigateur de n'utiliser que HTTPS pour ce nom pendant une durée donnée. Il renforce la redirection, mais il a une conséquence difficile à annuler : tant que la durée n'est pas écoulée, le navigateur refusera HTTP, même si vous corrigez ensuite la configuration.
add_header Strict-Transport-Security "max-age=300" always;Commencez par une durée courte (max-age=300, cinq minutes), puis allongez. Le paramètre includeSubDomains étend la règle à tous les sous-domaines : n'ajoutez-le qu'après avoir vérifié que chacun d'eux sert un HTTPS valide, sans quoi un service interne oublié devient inaccessible. Le paramètre preload est un engagement encore plus fort, qui s'inscrit dans des listes des navigateurs et ne se retire pas à volonté.
Comment vérifier le certificat réellement servi ?
Section intitulée « Comment vérifier le certificat réellement servi ? »Vérifiez avec un client qui valide la chaîne, jamais avec curl -k : -k désactive la validation, donc il ne prouve rien de ce que vous voulez mesurer. Trois informations se contrôlent : la chaîne remonte à l'autorité, le nom demandé figure dans les SAN, et la série (le numéro de série du certificat) a changé après le renouvellement.
En lab, l'autorité Pebble a un piège : sa racine change à chaque démarrage. Elle se récupère sur l'interface de gestion de Pebble au moment de la mesure, jamais en dur dans un script ou une procédure.
curl -sk https://127.0.0.1:15000/roots/0 -o racine.pemcurl -sS --cacert racine.pem --resolve app.lab.test:443:127.0.0.1 \ -o /dev/null -w '%{http_code}\n' https://app.lab.test/echo | openssl s_client -connect 127.0.0.1:443 -servername app.lab.test \ -CAfile racine.pem 2>/dev/null | openssl x509 -noout -serial -enddateLa première commande télécharge la racine : le -k n'y sert qu'à joindre l'interface de gestion de Pebble, pas à valider le service. La deuxième valide la chaîne sans -k et doit rendre le code 200. La troisième affiche la série et la date de fin du certificat réellement présenté ; relancez-la après un renouvellement et comparez : la série doit avoir changé. Un calcul d'expiration se prête aussi à la supervision.
echo | openssl s_client -connect 127.0.0.1:443 -servername app.lab.test 2>/dev/null \ | openssl x509 -noout -checkend 1209600L'option -checkend prend un nombre de secondes (1 209 600 correspondent à quatorze jours) et renvoie un code de sortie non nul si le certificat expire avant. Brancher ce code sur une alerte donne le filet de sécurité qui manque aujourd'hui : Let's Encrypt n'envoie plus de courriels d'expiration.
Vérifiez enfin ce qui doit survivre. Un reload ne doit rien casser ; un reboot de la machine non plus : le certificat et la configuration doivent être sur le disque, et le service activé au démarrage (systemctl is-enabled nginx). Ce sont les quatre tests de non-régression à rejouer après chaque modification : chaîne et SAN valides, nouvelle série sans redémarrage ni nouveau compte, clés protégées, TLS correct après reload puis reboot. Pour tester un changement sans entamer le quota d'une autorité réelle, utilisez son environnement de staging, présenté dans la leçon sur ACME.
Mettre en pratique : renouveler et servir un certificat Nginx
Section intitulée « Mettre en pratique : renouveler et servir un certificat Nginx »Le lab part d'une autorité ACME locale en marche, d'un client ACME disponible en image et de deux noms à servir, app.lab.test et api.lab.test. Aucun certificat n'existe : Nginx publie l'application en clair et sert déjà le chemin du défi. Vous obtenez les certificats, servez les deux noms en HTTPS, protégez les clés, et fournissez /usr/local/bin/renouveler-certificats, un point d'entrée que la validation appellera sans connaître votre mécanisme.
La validation note l'état et non les commandes tapées : le client valide la chaîne des deux noms sans -k, une nouvelle série est servie après le renouvellement sans redémarrer Nginx ni changer le compte ACME, les clés ne sont pas lisibles par tous, et TLS reste correct après un reload puis après un reboot.
À retenir
Section intitulée « À retenir »- Un
serverTLS reçoit la chaîne complète (ssl_certificate) et la clé (ssl_certificate_key), et le certificat doit porter tous les noms servis dans ses SAN. - La voie principale est un client ACME externe : stable et indépendant de la version ; certbot reste le plan B.
- Le module natif
ngx_http_acme_moduleest en preview (HTTP-01 et TLS-ALPN-01, pas de DNS-01) : ce n'est jamais une dépendance implicite d'une procédure de production. - Le défi HTTP-01 se sert en clair : une redirection globale vers HTTPS échoue à la première émission (rien n'écoute encore en 443) et dépend de ce que l'autorité suit ; réservez donc un
locationau chemin du défi, servi en clair, même si Let's Encrypt suit les redirections. - Un renouvellement se met en service par un reload, jamais un restart, et réutilise le compte ACME existant : se réinscrire consomme un quota.
- Avec des certificats de 45 jours, renouvelez vers le trentième jour, supervisez l'expiration (
openssl x509 -checkend) et ne comptez pas sur des courriels. - Vérifiez avec un client qui valide la chaîne (jamais
-k), comparez la série avant et après, protégez les clés (0600) et rejouez le test après reload et reboot. includeSubDomainsde HSTS exige d'avoir vérifié tous les sous-domaines concernés : commencez par unmax-agecourt.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Protéger et observer Nginx
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications