Ubuntu Server installe encore Nginx 1.28 dans ses dépôts officiels, une version vulnérable à la faille HTTP/2 Bomb (CVE-2026-49975) capable d’épuiser la mémoire d’un serveur en quelques secondes avec une seule connexion. Ce tutoriel détaille, à partir d’un cas réel rencontré sur Apollo, comment auditer les modules NginX existants, basculer proprement sur le dépôt officiel NginX mainline avec APT, puis valider et nettoyer l’installation.
Le symptôme : un serveur qui tombe sans raison apparente
En ce début de semaine, skyminds.net était indisponible au petit matin, deux jours de suite. C’est exceptionnellement rare : la configuration du serveur dédié OVH Apollo est stable et n’est jamais modifiée sur un coup de tête.
Après quelques heures à éplucher les logs et le statut des services, le constat était clair : des requêtes épuisaient les workers NginX à une vitesse anormale, sans pic de trafic visible côté Cloudflare ni signe d’attaque volumétrique classique. Le coupable n’était donc pas une simple charge excessive, mais un mécanisme bien plus sournois.
La cause : la faille HTTP/2 Bomb
Une attaque par déni de service baptisée HTTP/2 Bomb touche plusieurs serveurs web majeurs : NginX, Apache HTTPD, Microsoft IIS, Envoy et Cloudflare Pingora. Elle combine deux mécanismes connus du protocole HTTP/2 : la compression d’en-têtes HPACK et le contrôle de flux. L’attaquant alimente la table de compression avec une entrée volumineuse, puis envoie de nombreuses références d’un seul octet vers cette entrée, forçant le serveur à reconstruire des en-têtes massifs à chaque requête tout en maintenant la connexion ouverte grâce à des mises à jour de fenêtre périodiques. Résultat : un attaquant disposant d’une simple connexion domestique peut rendre un serveur indisponible en quelques secondes, sans authentification ni botnet.
Plusieurs éditeurs de sécurité utilisent l’identifiant CVE-2026-49975 comme nom générique de cette chaîne d’attaque. Dans les faits, NginX a livré son correctif sans CVE dédiée, tandis que ce numéro concerne plus précisément Apache HTTP Server (module mod_http2) ; Envoy a reçu son propre identifiant. Le ratio d’amplification mesuré reste plus modeste sur NginX que sur les autres serveurs concernés, mais suffisant pour dégrader durablement le service sous une attaque soutenue.
Pourquoi Ubuntu Server reste vulnérable
NginX a corrigé le problème dans la version 1.29.8 (7 avril 2026), en introduisant la directive max_headers, activée par défaut à 1000 lignes d’en-têtes par requête. Si la limite est atteinte, NginX renvoie simplement une erreur 400 Bad Request au lieu d’allouer indéfiniment de la mémoire.
Le problème, c’est qu’Ubuntu Server 26.04 LTS ne fournit que Nginx 1.28.3 dans son dépôt principal, qui n’est donc pas corrigé. Ubuntu 24.04 LTS et les versions stables de Debian sont logées à la même enseigne, avec des versions encore plus anciennes. La solution la plus durable consiste à arrêter d’utiliser les paquets NginX d’Ubuntu pour basculer sur le dépôt officiel NginX mainline, qui suit les correctifs de sécurité bien plus rapidement que les dépôts de distribution.
Avant de modifier quoi que ce soit : sauvegarder la configuration existante
Changer de dépôt va remplacer le paquet nginx et supprimer les paquets de modules tiers packagés par Ubuntu. Avant de toucher à quoi que ce soit, on sauvegarde l’intégralité de la configuration :
sudo cp -a /etc/nginx /etc/nginx.bak-$(date +%F)Langage du code : JavaScript (javascript)
Cette copie permet de revenir en arrière instantanément en cas de souci, simplement en restaurant le dossier et en réinstallant les paquets Ubuntu d’origine.
Étape 1 : auditer et neutraliser les modules NginX tiers existants
Ubuntu packages les modules NginX différemment du dépôt officiel : plusieurs fonctionnalités sont fournies par des paquets séparés (libnginx-mod-*), alors que le paquet mainline les regroupe ou les omet selon les cas. On fait d’abord un état des lieux des modules réellement chargés :
ls -lah /etc/nginx/modules-enabled/
total 28K
drwxr-xr-x 2 root root 4.0K Jun 9 06:42 .
drwxr-xr-x 10 root root 4.0K Jun 9 06:42 ..
lrwxrwxrwx 1 root root 62 Apr 25 10:45 50-mod-http-brotli-filter.conf -> /usr/share/nginx/modules-available/mod-http-brotli-filter.conf
lrwxrwxrwx 1 root root 62 Apr 25 10:45 50-mod-http-brotli-static.conf -> /usr/share/nginx/modules-available/mod-http-brotli-static.conf
lrwxrwxrwx 1 root root 54 Sep 3 2023 50-mod-http-geoip.conf -> /usr/share/nginx/modules-available/mod-http-geoip.conf
lrwxrwxrwx 1 root root 55 Apr 23 18:38 50-mod-http-geoip2.conf -> /usr/share/nginx/modules-available/mod-http-geoip2.conf
lrwxrwxrwx 1 root root 68 Dec 17 2024 50-mod-http-headers-more-filter.conf -> /usr/share/nginx/modules-available/mod-http-headers-more-filter.conf
lrwxrwxrwx 1 root root 61 Sep 3 2023 50-mod-http-image-filter.conf -> /usr/share/nginx/modules-available/mod-http-image-filter.conf
lrwxrwxrwx 1 root root 60 Sep 3 2023 50-mod-http-xslt-filter.conf -> /usr/share/nginx/modules-available/mod-http-xslt-filter.conf
lrwxrwxrwx 1 root root 48 Sep 3 2023 50-mod-mail.conf -> /usr/share/nginx/modules-available/mod-mail.conf
lrwxrwxrwx 1 root root 50 Sep 3 2023 50-mod-stream.conf -> /usr/share/nginx/modules-available/mod-stream.conf
lrwxrwxrwx 1 root root 56 Sep 3 2023 70-mod-stream-geoip.conf -> /usr/share/nginx/modules-available/mod-stream-geoip.conf
lrwxrwxrwx 1 root root 57 Apr 23 18:38 70-mod-stream-geoip2.conf -> /usr/share/nginx/modules-available/mod-stream-geoip2.confLangage du code : JavaScript (javascript)
Onze modules sont déclarés, ce qui ne veut pas dire qu’ils sont tous utilisés activement dans la configuration. On vérifie ensuite quels modules sont effectivement chargés via load_module :
grep -R "load_module" /etc/nginx/nginx.conf /etc/nginx/modules-enabled -nLangage du code : JavaScript (javascript)
Plutôt que de supprimer ces modules directement, on les déplace dans un dossier temporaire, puis on teste la configuration :
sudo mkdir -p /root/nginx-modules-disabled
sudo mv /etc/nginx/modules-enabled/50-mod-http-brotli-filter.conf /root/nginx-modules-disabled/ 2>/dev/null
sudo mv /etc/nginx/modules-enabled/50-mod-http-brotli-static.conf /root/nginx-modules-disabled/ 2>/dev/null
sudo mv /etc/nginx/modules-enabled/50-mod-http-headers-more-filter.conf /root/nginx-modules-disabled/ 2>/dev/null
sudo mv /etc/nginx/modules-enabled/50-mod-http-geoip2.conf /root/nginx-modules-disabled/ 2>/dev/null
sudo mv /etc/nginx/modules-enabled/70-mod-stream-geoip2.conf /root/nginx-modules-disabled/ 2>/dev/null
sudo nginx -t && sudo systemctl restart nginxLangage du code : JavaScript (javascript)
2026/06/09 09:27:29 [emerg] 1611560#1611560: unknown directive "more_set_headers" in /etc/nginx/snippets/exclusions.conf:221
nginx: configuration file /etc/nginx/nginx.conf test failedLangage du code : PHP (php)
Le module headers-more était bien utilisé : deux directives more_set_headers non essentielles trônaient dans un snippet. Elles ont été supprimées. Les directives liées à Brotli ont également été retirées, puisque c’est désormais Cloudflare qui gère la compression en amont — inutile de la dupliquer côté origine.
Pour les modules restants, on vérifie par un simple grep qu’ils ne sont référencés nulle part dans la configuration avant de les considérer comme inutiles :
sudo grep -R "more_set_headers\|more_clear_headers\|headers_more" /etc/nginx -n || true
sudo grep -R "geoip\|geoip2" /etc/nginx -n || true
sudo grep -R "image_filter\|xslt\|mail\s*{\|stream\s*{" /etc/nginx -n || trueLangage du code : JavaScript (javascript)
Aucune de ces directives n’apparaît dans la configuration active (à part les déclarations load_module elles-mêmes). Le terrain est dégagé : on peut basculer sur le dépôt mainline sans craindre de casser une fonctionnalité encore en service.
Étape 2 : ajouter le dépôt officiel NginX mainline
On installe d’abord les outils nécessaires à l’ajout d’un dépôt signé, puis on récupère la clé de signature officielle de NginX :
sudo apt update
sudo apt install -y curl ca-certificates gnupg lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/nullLangage du code : JavaScript (javascript)
On déclare ensuite le dépôt mainline comme source APT, en l’associant à la clé qu’on vient d’importer :
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.listLangage du code : PHP (php)
Sans précaution supplémentaire, APT pourrait continuer à privilégier le paquet d’Ubuntu lors d’une future mise à jour. On épingle donc le dépôt nginx.org pour qu’il ait toujours la priorité :
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
| sudo tee /etc/apt/preferences.d/99nginxLangage du code : PHP (php)
Avant d’installer pour de vrai, une simulation permet de vérifier ce qu’APT va réellement modifier — particulièrement utile pour repérer à l’avance les paquets qui seront supprimés :
sudo apt update
sudo apt install --simulate nginx
Une fois la simulation jugée cohérente, on procède à l’installation réelle :
sudo apt install nginx
Upgrading:
nginx
REMOVING:
libnginx-mod-http-brotli-filter libnginx-mod-http-headers-more-filter libnginx-mod-stream nginx-core
libnginx-mod-http-brotli-static libnginx-mod-http-image-filter libnginx-mod-stream-geoip
libnginx-mod-http-geoip libnginx-mod-http-xslt-filter libnginx-mod-stream-geoip2
libnginx-mod-http-geoip2 libnginx-mod-mail nginx-common
Summary:
Upgrading: 1, Installing: 0, Removing: 13, Not Upgrading: 0
Download size: 1264 kB
Space needed: 1348 kBLangage du code : JavaScript (javascript)
APT supprime bien tous les paquets de modules Ubuntu au profit du paquet nginx.org, conformément à ce qu’on avait anticipé à l’étape précédente.
Étape 3 : valider et relancer le service
On recharge systemd, on réinitialise l’état du service, puis on redémarre proprement :
sudo systemctl daemon-reload
sudo systemctl reset-failed nginx.service
sudo systemctl restart nginx
sudo systemctl status nginx --no-pagerLangage du code : CSS (css)
● nginx.service - nginx - high performance web server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Wed 2026-06-10 14:26:00 CEST; 16ms ago
Docs: https://nginx.org/en/docs/
Process: 2155646 ExecStart=/usr/sbin/nginx -c ${CONFFILE} (code=exited, status=0/SUCCESS)
Main PID: 2155647 (nginx)
Tasks: 19 (limit: 34590)
Memory: 143.6M (peak: 143.6M)
CGroup: /system.slice/nginx.service
├─2155647 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
├─2155648 "nginx: worker process"
├─2155649 "nginx: worker process"
├─2155670 "nginx: cache manager process"
└─2155672 "nginx: cache loader process"
Jun 10 14:26:00 apollo systemd[1]: Starting nginx.service - nginx - high performance web server...
Jun 10 14:26:00 apollo systemd[1]: Started nginx.service - nginx - high performance web server.Langage du code : JavaScript (javascript)
On confirme enfin la version installée :
nginx -V
nginx version: nginx/1.31.2
built by gcc 15.2.0 (Ubuntu 15.2.0-16ubuntu1)
built with OpenSSL 3.5.5 27 Jan 2026
TLS SNI support enabledLangage du code : JavaScript (javascript)
Dans ce cas précis, la 1.31.1 était la dernière mainline disponible au moment de l’opération. Si vous suivez ce tutoriel aujourd’hui, apt install nginx installera directement la dernière version publiée sur nginx.org — au moment de la rédaction, la 1.31.2 du 17 juin 2026, qui corrige en plus trois failles de sécurité sans rapport avec HTTP/2 Bomb. Vérifiez systématiquement le numéro de version après installation plutôt que de vous fier à un chiffre lu dans un article.
La protection max_headers est active par défaut dès la 1.29.8, sans configuration supplémentaire. Pour la rendre explicite dans votre configuration — ce qui facilite l’audit futur — ajoutez la directive dans le bloc http :
http {
max_headers 1000;
}
Puis rechargez sans coupure de service :
sudo nginx -t && sudo systemctl reload nginx
Étape 4 : nettoyer les anciens fichiers de modules
L’installation du paquet nginx.org a désactivé les anciens fichiers de modules sans les supprimer : ils portent désormais l’extension .removed et ne sont plus chargés.
ls -lah /etc/nginx/modules-enabled/
50-mod-http-brotli-filter.conf.removed
50-mod-http-brotli-static.conf.removed
50-mod-http-geoip.conf.removed
50-mod-http-geoip2.conf.removed
50-mod-http-headers-more-filter.conf.removed
50-mod-http-image-filter.conf.removed
50-mod-http-xslt-filter.conf.removed
50-mod-mail.conf.removed
50-mod-stream.conf.removed
70-mod-stream-geoip.conf.removed
70-mod-stream-geoip2.conf.removedLangage du code : CSS (css)
On les supprime purement et simplement, puisqu’ils ne pointent plus vers rien d’actif :
sudo find /etc/nginx/modules-enabled -type l -name '*.removed' -deleteLangage du code : JavaScript (javascript)
Dernière vérification, pour s’assurer qu’aucune trace de configuration obsolète ne subsiste :
sudo grep -R "brotli\|more_set_headers\|more_clear_headers\|geoip2\|image_filter\|xslt" /etc/nginx -n || trueLangage du code : JavaScript (javascript)
Il ne reste qu’une ancienne référence à Brotli dans un commentaire de configuration, qui n’est plus chargée activement. L’installation est propre.
Et si vous êtes encore sous Ubuntu 24.04 LTS ou Debian ?
La procédure reste identique : seul le codename récupéré par lsb_release -cs change automatiquement (noble pour Ubuntu 24.04, bookworm ou trixie pour Debian). Si vous partez d’une installation initiale plutôt que d’une migration, l’article installer NginX avec support HTTP/2 et certificat SSL sous Debian couvre la mise en place complète de la pile, à laquelle vous pouvez ensuite appliquer ce basculement vers le dépôt mainline.
Une couche de défense supplémentaire, en complément du correctif
Patcher NginX règle le problème côté origine, mais une défense en profondeur reste utile, surtout si d’autres briques de votre infrastructure (proxy, load balancer) ne sont pas toutes patchables aussi vite. Cloudflare a indiqué publiquement que son architecture et ses mécanismes anti-DDoS détectent automatiquement ce type de schéma d’attaque, sans nécessiter de correctif côté client.
Si vous utilisez déjà Cloudflare devant NginX, vérifiez simplement que le proxy reste activé — et si vous rencontrez des erreurs 526 en sortie, l’article résoudre l’erreur 526 entre Cloudflare et NginX couvre ce cas précis.
Qui était exposé au moment de la divulgation
| Serveur | Statut à la divulgation | Version corrigée |
|---|---|---|
| NginX | Corrigé, sans CVE dédiée | 1.29.8 et suivantes |
| Apache HTTPD | Corrigé | 2.4.68 / mod_http2 2.0.41 |
| Microsoft IIS | Non corrigé à la divulgation | — |
| Envoy | Non corrigé à la divulgation | CVE distincte (CVE-2026-47774) |
| Cloudflare Pingora | Atténué côté infrastructure | — |
Checklist avant de considérer le serveur protégé
- Sauvegarder /etc/nginx avant toute modification
- Identifier les modules réellement chargés via load_module
- Désactiver (sans supprimer) les modules, puis tester avec nginx -t
- Ajouter le dépôt nginx.org et l’épingler avec une priorité APT élevée
- Simuler l’installation avant de l’exécuter pour de vrai
- Valider la configuration, redémarrer le service, vérifier nginx -V
- Nettoyer les anciens fichiers de modules .removed
- Surveiller les logs d’erreur les jours suivants
Foire aux questions
Faut-il utiliser la branche mainline ou stable de NginX ?
Pour un correctif de sécurité urgent comme celui-ci, la mainline reçoit les correctifs immédiatement. La branche stable ne reçoit que des correctifs critiques rétroportés, parfois avec un léger délai. Sur un serveur de production que vous surveillez régulièrement, la mainline est un choix raisonnable.
Ce basculement va-t-il casser ma configuration WordPress ?
Non, tant que vos blocs server et vos directives FastCGI restent identiques. Seuls les modules tiers packagés séparément par Ubuntu disparaissent : si vous n’en utilisez aucun, comme c’était le cas ici une fois nettoyé, le site continue de fonctionner sans aucune modification de configuration.
Le dépôt mainline survit-il à une prochaine mise à jour Ubuntu ?
Oui, à condition de conserver le fichier d’épinglage dans /etc/apt/preferences.d/. Lors d’une migration de version Ubuntu, pensez simplement à vérifier que le codename dans /etc/apt/sources.list.d/nginx.list correspond toujours à votre nouvelle version.
Cloudflare suffit-il sans patcher NginX ?
Non. Cloudflare réduit le risque si le trafic passe par son proxy, mais toute connexion directe au serveur (IP d’origine exposée, autre vhost, accès interne) reste vulnérable. Le correctif côté NginX demeure la seule protection fiable et durable.
Conclusion
Le dépôt mainline de NginX règle durablement le problème, et la méthode d’audit des modules avant migration évite les mauvaises surprises au redémarrage. Une fois cette base posée, c’est aussi l’occasion de revoir les autres réglages réseau du serveur : l’article optimiser TCP sous Linux complète bien cette démarche, tout comme le suivi régulier des versions PHP si vous gérez plusieurs sites WordPress sur la même machine, comme détaillé dans installer PHP 8.5 sous Ubuntu Server. Pensez aussi à surveiller le changelog nginx.org plutôt que d’attendre la prochaine panne pour découvrir un correctif déjà disponible depuis des semaines.
Besoin d’un audit de votre serveur ou de votre hébergement ?
Suivre chaque CVE NginX, Apache ou OpenSSL demande du temps que vous n’avez pas forcément. J’accompagne les sites WordPress et WooCommerce sur :
- L’audit et le durcissement de la configuration NginX et PHP-FPM
- La migration vers des versions LTS récentes (Ubuntu, PHP, MySQL/MariaDB)
- La mise en place de sauvegardes fiables avant toute opération sensible
- Le suivi et l’application des correctifs de sécurité critiques
Pour discuter de votre infrastructure, contactez-moi.
