Cette semaine, deux failles critiques ont touché le cœur de PHP (CVE-2026-109618 et CVE-2026-109619, notées 9,2 sur 10). Nous les avons corrigées sur notre infrastructure. Mais l’épisode pose une question de fond, qui dépasse largement PHP : pourquoi tant de sites exécutent-ils du code à chaque visite, pour afficher une page qui n’a pas changé depuis des semaines ?
Le constat : regénérer X fois le même contenu
Sur un site WordPress, PrestaShop ou une application PHP classique, chaque visite d’une page déclenche le même travail : démarrer PHP, charger le CMS et ses extensions, interroger la base de données, assembler le HTML, puis l’envoyer. Le schéma est identique avec une application Node.js (Express, NestJS, Next.js en rendu serveur), Python (Django, FastAPI), Go, Java ou Ruby : le serveur d’application reconstruit la page à chaque requête.
Pour une page « Qui sommes-nous », une fiche service ou un article de blog, le résultat est pourtant strictement identique d’une visite à l’autre, jusqu’à la prochaine modification. Autrement dit, on recalcule mille fois ce qui pourrait être calculé une seule fois. C’est un coût en ressources serveur et en temps de réponse. C’est aussi, comme le montrent ces failles, un coût en sécurité.
Le lien avec les failles PHP « php2shell »
Selon l’avis officiel du projet PHP, ces deux failles pouvaient mener à une exécution de code à distance, sans authentification, par une simple requête HTTP adressée à PHP. Les atténuations partielles listées par le projet portent notamment sur la gestion des envois de données : désactiver session.upload_progress et réduire post_max_size.
Le point clé est le suivant : la faille n’est exploitable que là où le code s’exécute. Sur un site où chaque URL passe par PHP, toutes les pages sont une porte d’entrée potentielle. Tant que le correctif n’est pas appliqué, la seule option sûre est souvent de couper le site entier.
Sur un site où les pages sont servies sous forme de fichiers HTML déjà générés, et où le code dynamique n’intervient que pour le formulaire de contact ou l’envoi de fichiers, la situation est tout autre : on désactive ce formulaire quelques heures, et le reste du site continue de fonctionner normalement.
Pas seulement PHP : Node.js, Go, Python… même combat
Ce serait une erreur de lire cet épisode comme un problème propre à PHP. Tout environnement d’exécution exposé sur le web connaîtra un jour une faille critique : l’interpréteur ou la machine virtuelle (Node.js, Python, la JVM), la bibliothèque standard (le serveur HTTP de Go, par exemple), le framework (Express, Django, Spring, Laravel…) ou l’une des centaines de dépendances qu’il embarque. La même semaine d’ailleurs, le projet Node.js avait annoncé des correctifs de sécurité classés « HIGH » pour ses versions 22, 24 et 26.
Changer de langage ne supprime donc pas le risque : cela le déplace. Ce qui réduit réellement l’exposition, c’est la quantité de code exécutée pour chaque requête publique, quel que soit le langage. Une application Go ou Node.js qui reconstruit chaque page à chaque visite présente le même profil qu’un WordPress classique : toute la surface du site dépend de la sécurité d’un seul composant.
L’alternative : générer à la mise à jour, pas à la visite
Le principe est simple : la page est produite une seule fois, au moment où son contenu change (publication, correction, mise à jour d’un prix), puis servie telle quelle à tous les visiteurs. Plusieurs approches existent :
- Générateur de site statique (Hugo en Go, Eleventy ou Astro en Node.js, Pelican ou MkDocs en Python…) : les pages sont construites à partir de fichiers ou d’un CMS « headless » et publiées en HTML. Le langage du générateur n’est utilisé qu’au moment de la construction, jamais face aux visiteurs.
- WordPress en génération statique : vous gardez l’interface d’administration que vous connaissez, mais le site public est exporté en HTML à chaque publication. L’administration peut alors être placée à l’abri, sur une adresse restreinte. C’est l’approche « headless » que nous utilisons pour ce blog, détaillée dans notre article Headless CMS : notre blog en est un exemple.
- Régénération à la demande : seule la page modifiée (et les listes qui la contiennent) est reconstruite, au lieu de tout le site. Plusieurs frameworks Node.js le proposent nativement.
Côté serveur, le résultat ressemble à ceci : Nginx sert directement les fichiers HTML, et seul un point d’entrée précis est confié au code dynamique, qu’il soit en PHP ou dans un autre langage.
server {
root /var/www/mon-site/public; # pages HTML générées
location / {
try_files $uri $uri/index.html =404;
}
# Variante PHP : seul le formulaire de contact passe par PHP-FPM
location = /contact/envoi.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/mon-site/php/envoi.php;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
# Variante Node.js, Go ou Python : une petite API derrière un proxy
location /api/contact {
proxy_pass http://127.0.0.1:3000;
}
# Aucun autre script n'est exécuté
location ~ \.php$ {
return 404;
}
}
Le jour où une faille est annoncée dans le langage ou le framework utilisé, la mesure d’urgence tient en quelques lignes, sans toucher au reste du site :
location /api/contact {
return 503; # formulaire suspendu le temps de la mise à jour
}
Le formulaire affiche alors un message invitant à écrire par e-mail ou à appeler, et le reste du site reste en ligne. C’est une interruption ciblée de quelques heures au lieu d’un site entier hors ligne ou exposé. Le même raisonnement vaut pour l’envoi de fichiers, souvent la fonction la plus sensible : isolée sur son propre point d’entrée, elle peut être suspendue seule.
Et le cache de pages, ça ne suffit pas ?
Les extensions de cache, le cache côté serveur ou un CDN évitent bien de recalculer les pages pour la plupart des visiteurs, et c’est déjà un vrai progrès en performance. Mais l’application reste joignable derrière : une requête POST, un paramètre ajouté à l’URL ou un cookie particulier suffisent souvent à contourner le cache et à l’atteindre, qu’elle soit en PHP, en Node.js ou en Python. Pour la sécurité, ce n’est donc pas équivalent à un site où aucun code n’est exécuté sur les pages publiques.
Le code dynamique, oui, mais avec parcimonie
Il ne s’agit pas de bannir PHP ni les langages dynamiques : nous développons tous les jours en PHP, Node.js, Python ou Go, et beaucoup de projets en ont besoin. Un espace client, un panier, un moteur de recherche, une application métier sont dynamiques par nature. La bonne question à se poser est plutôt : ce contenu change-t-il d’un visiteur à l’autre, ou d’une seconde à l’autre ?
- Non (pages vitrines, articles, fiches produits peu mouvantes) : générez-le à la mise à jour.
- Oui (formulaires, comptes, paiement, recherche) : gardez-le dynamique, dans le langage le plus adapté, mais isolez-le sur des adresses précises, que l’on peut surveiller, limiter et couper indépendamment.
Les bénéfices dépassent la sécurité : des pages servies plus vite, moins de ressources serveur consommées, un site qui encaisse mieux les pics de trafic, et moins de dépendance aux mises à jour urgentes du langage, du framework ou du CMS.
Notre avis d’agence
Les failles « php2shell » sont corrigées sur notre infrastructure. Mais la prochaine alerte concernera peut-être Node.js, Python, Go ou une dépendance que personne ne surveillait. La meilleure faille reste celle qui ne peut pas toucher votre site faute de point d’entrée. Concevoir un site en réservant le code dynamique à ce qui en a vraiment besoin, c’est limiter d’avance l’impact de la prochaine alerte, quel que soit le langage concerné.
Vous vous demandez quelles parties de votre site pourraient être générées à la mise à jour plutôt qu’à chaque visite ? Contactez-nous : nous pouvons étudier votre site et vous proposer l’architecture la plus adaptée.
À lire aussi
- Headless CMS : notre blog en est un exemple
- Comment Astro et une API PHP ont remplacé un site WordPress monopage
- Site statique, site dynamique, CMS : que choisir ?
Sources
À lire aussi