En septembre 2025, l’une des entreprises d’infrastructure Internet les plus critiques a connu une panne qui n’a pas été causée par des hackers sophistiqués, des attaques DDoS massives ou des pannes de serveurs. Au lieu de cela, Cloudflare — l’entreprise qui dévie régulièrement des attaques de plusieurs térabits par seconde et maintient des millions de sites web en ligne — a été mise hors service par une erreur fondamentale de programmation React que de nombreux développeurs juniors apprennent à éviter dès leur première semaine.
La réalité choquante
L’ironie est presque comique : Cloudflare, qui a réussi à se défendre contre des attaques DDoS de 7,3 térabits par seconde, a été mis hors ligne par un hook useEffect React avec un tableau de dépendances inapproprié. Ce n’était pas une escouade d’élite de hackers étatiques — c’était un anti-pattern classique de développement frontend qui a créé une boucle infinie d’appels API.
Source : Les détails techniques et la chronologie référencés dans cet article sont basés sur le rapport d’incident officiel de Cloudflare : “A deep dive into Cloudflare’s September 12, 2025 dashboard and API outage”
Comprendre l’erreur technique
Le pattern de code problématique
Le problème provenait de ce pattern React courant :
// Version simplifiée du code problématique
function Dashboard() {
const params = { // Cet objet est recréé à chaque rendu
organizationId: user.orgId,
filters: currentFilters
};
useEffect(() => {
// Cette fonction appelle l'API
fetchDashboardData(params);
}, [params]); // ← Le problème est ici
return "Dashboard rendered";
}
Pourquoi cela crée une boucle infinie
Le hook useEffect compare les dépendances en utilisant une comparaison superficielle. Voici ce qui se passe :
- Le composant fait un rendu → Crée un nouvel objet
params - useEffect s’exécute → Récupère les données, mettant potentiellement à jour l’état
- La mise à jour d’état déclenche un re-rendu → Crée encore un autre nouvel objet
params - React compare les dépendances → La référence de
paramsa changé - useEffect s’exécute à nouveau → Retour à l’étape 2, créant une boucle infinie
Pourquoi l’objet params est-il recréé à chaque fois ?
En JavaScript, les littéraux d’objet comme { organizationId: user.orgId, filters: currentFilters } créent un nouvel objet en mémoire chaque fois qu’ils sont exécutés. Même si les valeurs à l’intérieur sont identiques, la référence de l’objet est différente.
// Chaque fois que cette fonction s'exécute, un NOUVEL objet est créé
const params = { organizationId: user.orgId, filters: currentFilters };
// C'est équivalent à :
const params = new Object();
params.organizationId = user.orgId;
params.filters = currentFilters;
Comparaison de référence mémoire :
// Ces objets ont le même contenu mais des références mémoire différentes
const obj1 = { name: "John" };
const obj2 = { name: "John" };
console.log(obj1 === obj2); // false - emplacements mémoire différents
// Seule la même référence retourne true
const obj3 = obj1;
console.log(obj1 === obj3); // true - même référence mémoire
Le useEffect de React utilise Object.is() (similaire à ===) pour comparer les dépendances. Puisqu’un nouvel objet params est créé à chaque rendu, React pense que la dépendance a changé, même si les valeurs à l’intérieur peuvent être identiques.
Même si l’objet params contient les mêmes valeurs, React le voit comme « différent » car c’est une nouvelle référence d’objet en mémoire à chaque fois.
L’échelle du problème
Selon le rapport d’incident de Cloudflare, cette simple erreur a abouti à :
- Des milliers d’appels API par minute depuis une seule session de tableau de bord
- Surcharge complète de l’API multipliée par tous les utilisateurs
- Pannes en cascade à travers leur infrastructure
- Panne mondiale affectant des millions de sites web
Les solutions correctes
Solution 1 : Mémoïser la dépendance
import { useMemo, useEffect } from 'react';
function Dashboard() {
const params = useMemo(() => ({
organizationId: user.orgId,
filters: currentFilters
}), [user.orgId, currentFilters]); // Ne recrée que quand ceux-ci changent
useEffect(() => {
fetchDashboardData(params);
}, [params]);
return "Dashboard rendered";
}
Solution 2 : Séparer les dépendances
function Dashboard() {
useEffect(() => {
const params = {
organizationId: user.orgId,
filters: currentFilters
};
fetchDashboardData(params);
}, [user.orgId, currentFilters]); // Dépendances directes
return "Dashboard rendered";
}
Solution 3 : Utiliser useCallback pour les fonctions
import { useCallback, useEffect } from 'react';
function Dashboard() {
const fetchData = useCallback(async () => {
const params = {
organizationId: user.orgId,
filters: currentFilters
};
await fetchDashboardData(params);
}, [user.orgId, currentFilters]);
useEffect(() => {
fetchData();
}, [fetchData]);
return "Dashboard rendered";
}
Les leçons d’ingénierie plus larges
1. Échecs de revue de code
Comment cette boucle infinie évidente a-t-elle pu arriver en production ? L’incident met en lumière plusieurs échecs de processus d’ingénierie :
- Les tests de développement local auraient dû immédiatement montrer des milliers de requêtes réseau
- Les processus de revue de code auraient dû attraper les anti-patterns React fondamentaux
- Les environnements de staging auraient dû répliquer les patterns de charge de production
- Les systèmes de monitoring auraient dû alerter sur des patterns d’utilisation API inhabituels
2. Le problème du troupeau affolé
Quand Cloudflare a tenté de corriger le problème en vidant les sessions utilisateurs, ils ont par inadvertance créé un problème de « troupeau affolé » — des millions d’utilisateurs se réauthentifiant simultanément quand le service est revenu en ligne, causant une seconde panne.
3. Limitation de débit et disjoncteurs
L’incident a révélé que les APIs internes de Cloudflare manquaient de :
- Limitation de débit appropriée pour prévenir les abus
- Disjoncteurs pour échouer gracieusement sous charge
- Mécanismes de rollback automatique pour les déploiements problématiques
Stratégies de prévention
Pour les développeurs
- Utilisez les règles ESLint comme
exhaustive-depspour attraper les problèmes de dépendances - Installez React Developer Tools pour surveiller les re-renders de composants
- Ajoutez du monitoring réseau pour attraper les patterns API inhabituels pendant le développement
- Pratiquez la programmation défensive avec des error boundaries appropriés
Pour les équipes d’ingénierie
- Implémentez des déploiements progressifs au lieu de déploiements globaux instantanés
- Mettez en place un monitoring approprié pour les patterns d’utilisation API
- Établissez des checklists de revue de code pour les anti-patterns React courants
- Créez des environnements de test de charge qui simulent l’utilisation réelle
Configuration ESLint essentielle
{
"extends": ["plugin:react-hooks/recommended"],
"rules": {
"react-hooks/exhaustive-deps": "error"
}
}
Un moment d’apprentissage critique
Cet incident sert de rappel puissant de comment des erreurs de programmation fondamentales peuvent avoir des impacts mondiaux massifs. La panne a affecté des millions de sites web et d’innombrables entreprises dans le monde entier, le tout à cause d’une erreur qui aurait pu être attrapée avec des outils et processus appropriés.
Comme l’a commenté ThePrimeagen, un YouTuber tech renommé et célèbre, sur l’incident : « Nous avons tous fait cette erreur — je suis juste content d’avoir attrapé la mienne en développement, pas en production. »
Points clés à retenir
- Les connaissances fondamentales comptent — Même à l’échelle entreprise, les principes de programmation de base sont critiques
- L’outillage est essentiel — ESLint, React DevTools et un monitoring approprié préviennent ces problèmes
- Les échecs de processus composent les échecs techniques — Plusieurs filets de sécurité ont échoué simultanément
- Le déploiement progressif sauve des vies — Les déploiements globaux instantanés sont dangereux pour l’infrastructure critique
- La puissance de React requiert de la responsabilité — La flexibilité du framework exige des pratiques de développement disciplinées
Aller de l’avant
Cloudflare a depuis implémenté :
- Argo Rollouts pour les rollbacks automatiques de déploiement
- Monitoring amélioré pour les patterns d’utilisation API
- De meilleurs patterns de limitation de débit et de disjoncteurs
- Processus de revue de code améliorés pour les changements frontend
L’incident sert de rappel puissant que dans notre monde interconnecté, même les plus petits changements de code peuvent avoir des impacts mondiaux massifs. Que vous construisiez un simple site web ou gériez une infrastructure critique, comprendre les fondamentaux de React et implémenter des processus d’ingénierie appropriés n’est pas juste une bonne pratique — c’est essentiel pour la stabilité d’Internet.
La prochaine fois que vous écrirez un hook useEffect, rappelez-vous : les ingénieurs de Cloudflare vérifient probablement aussi leurs tableaux de dépendances deux fois maintenant.