Nginx en reverse proxy avec Let's Encrypt SSL
Ceci met en place un reverse proxy Nginx qui transmet correctement la requête d'origine au backend, plus un TLS Let's Encrypt avec un renouvellement automatique réellement vérifié.
Le bloc serveur du reverse proxy
Le rôle du proxy est de terminer la connexion avec le client, d'en ouvrir une seconde vers votre backend, et de dire à ce backend la vérité sur la première.
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
}X-Forwarded-For donne au backend la véritable chaîne d'IP du client. Utilisez $proxy_add_x_forwarded_for, pas $remote_addr, pour qu'il s'ajoute à la valeur existante au lieu de l'écraser s'il y a déjà un proxy en amont de celui-ci. X-Forwarded-Proto est ce que la plupart des frameworks vérifient pour savoir si la requête d'origine était en HTTPS. Sans lui, une application derrière un proxy qui termine le TLS croit être toujours en HTTP simple, et tout middleware de "redirection vers https" tourne en boucle. Host compte aussi : de nombreux backends routent ou valident directement en fonction de cette valeur.
Si vous omettez proxy_set_header Host $host et que le backend utilise la valeur par défaut, l'ALLOWED_HOSTS de Django, l'autorisation d'hôte de Rails et la génération d'URL absolues de Next.js vont toutes casser d'une manière qui ressemble à un bug applicatif.
Obtenir un certificat avec certbot
J'utilise le plugin Nginx de certbot parce qu'il modifie directement vos blocs serveur existants (en ajoutant la directive listen 443 ssl, les chemins vers les certificats et la redirection), plutôt qu'une configuration séparée à réconcilier à la main.
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com -d www.app.example.comCertbot commence par demander une adresse e-mail (pour les notifications d'expiration ; une boîte que quelqu'un surveille vraiment). Puis il valide la propriété du domaine en HTTP sur le port 80 et obtient le certificat. Faites ensuite un nginx -t et relisez le diff, pour savoir exactement ce qui a changé.
Redirection HTTP vers HTTPS et HSTS
L'option de redirection de certbot fait l'essentiel du travail, mais j'ajoute toujours HSTS explicitement plutôt que de me fier aux valeurs par défaut, c'est cet en-tête qui empêche une attaque de downgrade de prendre pied.
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}N'ajoutez pas preload à l'en-tête HSTS tant que chaque sous-domaine que vous possédez ne peut pas servir du HTTPS indéfiniment. Entrer dans la liste de préchargement des navigateurs est facile, en sortir prend des mois, et entre-temps tout sous-domaine incapable de faire du TLS devient inaccessible.
Le renouvellement automatique, mais vérifié
Les certificats Let's Encrypt durent 90 jours. Le timer systemd de certbot renouvelle tout certificat à moins de 30 jours de son expiration, deux fois par jour, jusqu'à ce qu'une mise à jour du système change un chemin, qu'une règle de pare-feu bloque la validation sur le port 80, ou qu'une modification de configuration casse le bloc de location du challenge ACME.
systemctl list-timers | grep certbot
sudo systemctl status certbot.timerL'unité livrée par certbot ressemble grosso modo à ceci :
# /lib/systemd/system/certbot.timer
[Unit]
Description=Run certbot twice daily
[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true
[Install]
WantedBy=timers.targetLa commande que j'exécute sur chaque serveur, immédiatement, avant de faire confiance au dispositif :
sudo certbot renew --dry-run--dry-run exécute tout le chemin de renouvellement (validation, émission du certificat, hook de rechargement de Nginx) contre l'environnement de test de Let's Encrypt, sans toucher à votre vrai certificat ni à ses quotas. Si cette commande échoue, votre vrai renouvellement échouera aussi, simplement plus tard. Je l'exécute une fois à la mise en place, puis à chaque modification du pare-feu, de la configuration Nginx ou du DNS du serveur.
Branchez le renouvellement sur votre supervision au lieu de faire confiance à cron seul. Une vérification du code de sortie de certbot renew, ou une sonde hebdomadaire d'expiration TLS contre le endpoint en production, transforme un "personne ne l'a remarqué pendant quatre jours" en "quelqu'un a été alerté avant le client."
Durcissement de base : protocoles, chiffrements et en-têtes de sécurité
Une fois la terminaison TLS et le renouvellement solides, la dernière passe consiste à restreindre ce que le proxy accepte et à ajouter quelques en-têtes de sécurité.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Abandonner TLS 1.0 et 1.1 ne pose aucun problème pour un site grand public en 2026, les clients encore bloqués dessus représentent un risque de sécurité, quoi qu'il arrive. ssl_prefer_server_ciphers off laisse les clients modernes choisir leur chiffrement préféré, la meilleure pratique actuelle maintenant que les choix côté client sont généralement fiables. C'était l'inverse il y a quelques années, un bon rappel de revérifier les guides de durcissement tous les deux ou trois ans plutôt que de figer une configuration pour toujours.
Passez le résultat au test SSL Labs de Qualys ou à testssl.sh après chaque modification de ce bloc : cela permet de repérer les directives qui ne font pas ce que vous croyez, ou un ancien fragment de configuration ailleurs dans le fichier qui l'écrase silencieusement.
Ce sont les huit ou neuf mêmes directives à intégrer dans presque chaque configuration de proxy. Les avoir, c'est la différence entre une configuration TLS qu'on oublie et une qui revient un an plus tard sous forme de rapport d'incident.
Envie de faire tourner ça en production ?
Ce tutoriel couvre les concepts et l'architecture. Si vous voulez l'implémenter dans votre propre infrastructure, ou monter en compétence pour posséder ce sujet durablement, je propose du mentorat individuel construit autour de votre environnement réel, pas une formation générique.
Ce tutoriel
- Concepts clés et architecture principale
- Extraits de code illustratifs
- Le raisonnement derrière chaque décision
Mentorat individuel
- Des sessions de travail sur votre propre environnement
- Des réponses directes aux cas particuliers que vous rencontrez
- Un retour sur votre implémentation réelle
- Un accompagnement continu pendant que vous la construisez