📍 Article principal du parcours : Nginx : reverse proxy, HTTPS et configuration de A à Z
Cet article fait partie du parcours Nginx. Pour la vue d’ensemble, lisez d’abord le guide principal.
Colis Express tourne, chiffré, rapide, tolérant aux pannes. Mais une configuration qui « marche » n’est pas une configuration de production. Il reste à fermer les portes : n’accepter que des protocoles TLS modernes, poser les en-têtes de sécurité que les navigateurs attendent, masquer ce qui n’a pas à être public, et limiter le débit pour qu’un script abusif ne mette pas le service à genoux. C’est l’objet de ce dernier tutoriel : transformer un déploiement fonctionnel en déploiement sérieux. À la fin, votre serveur aura la check-list de durcissement essentielle posée et vérifiée.
🎯 Ce que vous allez apprendre
- Restreindre les protocoles et négocier un TLS moderne.
- Poser les en-têtes de sécurité (HSTS, anti-clickjacking, anti-sniffing).
- Masquer la version de Nginx et réduire la surface exposée.
- Limiter le débit des requêtes pour absorber les abus et le forçage.
- Savoir pourquoi l’agrafage OCSP n’a plus lieu d’être pour un certificat Let’s Encrypt.
🛠️ Ce que vous allez construire
Vous aurez un serveur qui n’accepte que TLS 1.2 et 1.3, qui annonce les bons en-têtes de sécurité, qui ne divulgue pas sa version, et qui plafonne le nombre de requêtes par client pour protéger l’API et la page de connexion. Vous saurez vérifier chaque réglage avec une commande.
Prérequis
- Un site Nginx en HTTPS et en reverse proxy (l’ensemble du parcours jusqu’ici).
- Accès
sudoet aisance avecnginx -tet le rechargement. - Niveau intermédiaire à avancé. Test express : si vous avez un site en HTTPS qui relaie une API, vous êtes prêt.
- ⏱️ Temps estimé : ~40 minutes.
Étape 1 — Restreindre les protocoles TLS
Les vieilles versions de TLS (1.0 et 1.1) sont obsolètes et vulnérables ; il faut les refuser. On ne garde que TLS 1.2 et TLS 1.3. Attention si vous avez utilisé Certbot : son fichier options-ssl-nginx.conf, inclus dans le bloc server du 443, définit déjà ssl_protocols, ssl_prefer_server_ciphers, ssl_session_cache et ssl_session_timeout. Ne les redéclarez pas une seconde fois dans le même bloc, sinon nginx -t échoue avec une erreur « duplicate » : ajustez les valeurs existantes (ou ce fichier). Le bloc ci-dessous montre les réglages visés — n’ajoutez que ceux qui manquent réellement chez vous.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_protocols TLSv1.2 TLSv1.3; exclut tout ce qui est antérieur. ssl_prefer_server_ciphers off; est le réglage moderne recommandé : avec TLS 1.3, on laisse le client choisir parmi des suites toutes sûres, ce qui simplifie et reste robuste. ssl_session_cache et ssl_session_timeout mutualisent les paramètres de session TLS entre les processus, ce qui accélère les reconnexions — un gain réel sur les liaisons à forte latence. Pour la liste exacte des suites de chiffrement, plutôt que de recopier une longue chaîne qui vieillira, le plus fiable est de générer une configuration adaptée à votre version de Nginx avec le générateur officiel de Mozilla (profil « intermediate »), cité en ressources.
✅ Point d’étape — Après rechargement, un test sur un analyseur SSL externe (ou
nmap --script ssl-enum-ciphers -p 443 votre-domaine) ne montre plus que TLS 1.2 et 1.3.
Étape 2 — Poser les en-têtes de sécurité
Les navigateurs respectent une série d’en-têtes HTTP qui durcissent le comportement de votre site contre des attaques courantes. Les poser ne coûte rien et élève nettement le niveau. Ajoutons-les dans le bloc server HTTPS.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
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;
Détaillons l’essentiel. Strict-Transport-Security (HSTS) ordonne au navigateur de ne plus jamais contacter le site en HTTP pendant un an — une protection forte contre la rétrogradation de protocole, à n’activer qu’une fois certain que tout fonctionne en HTTPS, car elle est difficile à annuler. X-Content-Type-Options: nosniff empêche le navigateur de « deviner » le type d’un fichier, ce qui bloque une classe d’attaques. X-Frame-Options: SAMEORIGIN interdit d’inclure votre site dans une iframe étrangère, contrant le détournement de clic. Referrer-Policy limite les informations de provenance transmises aux sites tiers. Le mot-clé always garantit que l’en-tête est envoyé même sur les réponses d’erreur.
Un mot sur la Content-Security-Policy (CSP), le plus puissant de ces en-têtes : elle contrôle finement quelles sources de scripts, styles et images le navigateur a le droit de charger, et constitue la meilleure défense contre l’injection de scripts. Mais une CSP mal réglée casse le site en silence ; elle se construit sur mesure, en testant d’abord en mode rapport. On la mentionne ici pour ne pas l’ignorer, sans la déployer à l’aveugle.
Étape 3 — Masquer la version et réduire la surface
Par défaut, Nginx annonce son numéro de version dans l’en-tête Server et sur ses pages d’erreur. Ce n’est pas une faille en soi, mais c’est un renseignement gratuit offert à un attaquant qui cherche des versions vulnérables. On le coupe globalement dans le bloc http de nginx.conf.
server_tokens off;
Avec server_tokens off;, l’en-tête Server n’affiche plus que nginx, sans version, et les pages d’erreur ne la divulguent plus. Dans le même esprit, on limite la taille des corps de requête pour éviter qu’un envoi massif ne sature le disque ou la mémoire, et on peut restreindre les méthodes HTTP acceptées si l’API n’en utilise que quelques-unes.
client_max_body_size 10m;
# Refuser les méthodes exotiques (garder GET, POST, PUT, DELETE, OPTIONS)
if ($request_method !~ ^(GET|POST|PUT|DELETE|OPTIONS)$) {
return 405;
}
client_max_body_size 10m; plafonne les téléversements à 10 Mo — à ajuster selon vos besoins réels. Le filtre sur $request_method renvoie un 405 pour toute méthode hors de la liste autorisée. Attention au contexte : client_max_body_size s’accepte au niveau http, server ou location, mais la directive if n’est valable qu’en contexte server ou location — jamais dans http, faute de quoi nginx -t renvoie « directive is not allowed here ». Placez donc ce filtre dans un location d’API bien cerné, et maniez le if avec discernement (il a des subtilités dans Nginx).
✅ Point d’étape —
curl -I https://colis-express.netrenvoieServer: nginxsans numéro de version, et les en-têtes de sécurité de l’étape 2 sont présents.
Étape 4 — Limiter le débit des requêtes
Une page de connexion ou une API publique sans limite de débit est une invitation au forçage de mots de passe et aux abus automatisés. Nginx sait plafonner le nombre de requêtes par client. On déclare d’abord une zone de limitation dans le bloc http.
limit_req_zone $binary_remote_addr zone=api_limite:10m rate=10r/s;
Cette directive crée une zone mémoire api_limite de 10 Mo qui suit chaque adresse IP cliente ($binary_remote_addr est une forme compacte de l’IP) et fixe une cadence de 10 requêtes par seconde. On l’applique ensuite au location sensible :
location /api/ {
limit_req zone=api_limite burst=20 nodelay;
proxy_pass http://colis_api/;
# ... les proxy_set_header habituels ...
}
Le burst=20 autorise une rafale de 20 requêtes au-dessus de la cadence avant de refuser, ce qui absorbe les pics légitimes (un utilisateur qui clique vite) sans bloquer. nodelay traite ces requêtes de rafale immédiatement plutôt que de les étaler. Au-delà, Nginx répond 503 automatiquement. Pour une page de connexion, on serait plus strict (par exemple rate=5r/m) afin de freiner sérieusement le forçage. Une directive complémentaire, limit_conn, plafonne le nombre de connexions simultanées par IP.
✅ Point d’étape — Une boucle de requêtes rapides vers
/api/finit par recevoir des503, preuve que la limitation s’active. Le trafic normal, lui, n’est pas gêné.
Étape 5 — Le cas OCSP : ne pas suivre les vieux tutoriels
Si vous lisez d’autres guides de durcissement, presque tous vous diront d’activer l’agrafage OCSP avec ssl_stapling on; pour accélérer la vérification de révocation du certificat. Pour un certificat Let’s Encrypt, ce conseil est aujourd’hui périmé.
Let’s Encrypt a fermé son service OCSP en 2025 : depuis le printemps de cette année, les certificats émis ne contiennent plus d’URL OCSP, et le service répondeur a été définitivement arrêté à l’été. La directive ssl_stapling on; n’a donc plus rien à agrafer — au mieux elle est inerte, au pire elle génère des avertissements dans vos logs. La révocation passe désormais par les listes de révocation (CRL), gérées côté navigateur, sans configuration serveur. Conclusion : n’ajoutez pas ssl_stapling pour un certificat Let’s Encrypt, et retirez-le si un ancien modèle de configuration l’a laissé traîner. C’est typiquement le genre de détail où un tutoriel récent et vérifié vous évite de copier une configuration obsolète.
🐞 Pièges fréquents
| Symptôme / erreur | Cause probable | Correctif |
|---|---|---|
| Le site est inaccessible après HSTS | HSTS activé alors que le HTTPS n’était pas fiable | Ne poser HSTS qu’une fois le HTTPS totalement stable ; max-age est dur à annuler |
En-têtes add_header qui disparaissent |
Un add_header dans un location remplace ceux du server |
Redéclarer les en-têtes nécessaires dans le location, ou les centraliser |
| Trafic légitime bloqué par la limite | rate/burst trop stricts |
Relever burst et ajuster rate selon l’usage réel |
| Avertissement OCSP dans les logs | ssl_stapling hérité d’un vieux modèle |
Retirer ssl_stapling pour les certificats Let’s Encrypt |
| 405 sur des requêtes normales | Filtre de méthodes trop restrictif | Inclure toutes les méthodes réellement utilisées par l’application |
🌍 Adaptation au contexte ouest-africain
La limitation de débit a une valeur particulière ici. Une attaque par forçage ou un robot mal réglé consomme votre bande passante et vos ressources serveur — c’est-à-dire votre argent, sur un VPS facturé à la capacité. Plafonner les requêtes protège donc autant votre budget que vos données. Attention toutefois à un effet de bord local : derrière certains opérateurs mobiles, de nombreux abonnés partagent une même IP publique (NAT à grande échelle). Une limite par IP trop serrée pourrait alors pénaliser des utilisateurs légitimes regroupés derrière la même adresse. Réglez la cadence en observant vos logs réels plutôt qu’en copiant une valeur arbitraire, et surveillez les 503 pour détecter une limite trop agressive.
✅ Récapitulatif
Vous avez restreint les protocoles à TLS 1.2 et 1.3, posé les en-têtes de sécurité (HSTS, anti-sniffing, anti-clickjacking, politique de référent), masqué la version de Nginx, plafonné la taille des requêtes, et mis en place une limitation de débit contre les abus. Vous savez aussi pourquoi l’agrafage OCSP appartient désormais au passé pour Let’s Encrypt. Avec ce dernier tutoriel, le parcours est complet : de l’installation à un déploiement de production durci, votre serveur Nginx tient debout.
🧾 Aide-mémoire
| Directive | Rôle |
|---|---|
ssl_protocols TLSv1.2 TLSv1.3; |
N’accepter que le TLS moderne |
add_header Strict-Transport-Security ... |
Forcer le HTTPS (HSTS) |
server_tokens off; |
Masquer la version de Nginx |
limit_req_zone + limit_req |
Limiter le débit par IP |
client_max_body_size 10m; |
Plafonner la taille des requêtes |
❌ ssl_stapling on; |
Inutile pour Let’s Encrypt (OCSP arrêté en 2025) |
💪 À vous de jouer
Appliquez une limitation bien plus stricte à une éventuelle route de connexion /api/login (par exemple 5 requêtes par minute), tout en gardant la limite plus souple sur le reste de l’API. Indice : on peut déclarer plusieurs zones et les appliquer à des location différents.
Voir une solution
# Dans le bloc http
limit_req_zone $binary_remote_addr zone=login_limite:10m rate=5r/m;
# Dans le server
location = /api/login {
limit_req zone=login_limite burst=3 nodelay;
proxy_pass http://colis_api/login;
}
On crée une zone dédiée login_limite très stricte (5 requêtes par minute), appliquée au seul point de connexion via un location = exact. Le reste de l’API garde sa limite plus généreuse. C’est ainsi qu’on freine le forçage de mots de passe sans gêner l’usage normal.
Tutoriels frères
- HTTPS avec Nginx et Let’s Encrypt grâce à Certbot — la base TLS que ce tutoriel renforce.
- Load balancing avec Nginx : répartir la charge — la résilience, complément de la sécurité.
Pour aller plus loin
- 🔝 Retour au guide principal : Nginx : reverse proxy, HTTPS et configuration de A à Z
- Documentation officielle : Mozilla SSL Configuration Generator et le module
limit_req. - Sur le même thème : Configurer des bouncers CrowdSec pour Nginx pour aller plus loin dans la défense active.
FAQ
Dois-je vraiment activer HSTS dès le départ ?
Activez-le seulement une fois certain que tout fonctionne durablement en HTTPS. HSTS dit au navigateur de refuser le HTTP pendant la durée max-age ; si vous deviez revenir en arrière, les visiteurs récents resteraient bloqués jusqu’à expiration. Commencez éventuellement par un max-age court, puis allongez-le.
La limitation de débit remplace-t-elle un pare-feu applicatif ?
Non, elle le complète. limit_req freine les abus volumétriques par IP ; un pare-feu applicatif (WAF) ou un outil comme CrowdSec analyse les schémas d’attaque et bannit les sources malveillantes. Les deux couches se renforcent.
Pourquoi mes add_header ne s’appliquent pas dans un location ?
Parce que add_header n’est pas hérité : dès qu’un location contient son propre add_header, les en-têtes définis au niveau server sont ignorés dans ce location. Il faut alors les y redéclarer, ou centraliser les en-têtes dans un fichier inclus partout.
Le durcissement peut-il casser mon site ?
Certaines mesures, oui, si elles sont mal réglées — HSTS prématuré, CSP trop stricte, filtre de méthodes trop large. D’où la règle : appliquer une mesure à la fois, tester, et vérifier les logs avant de passer à la suivante.