Un client d’un site de vente de pièces automobiles d’occasion a signalé, via le formulaire de contact, qu’en actualisant sa page panier il avait vu apparaître, pendant quelques secondes, des articles qu’il n’avait jamais ajoutés lui-même. Le signalement aurait pu être ignoré comme un bug d’affichage mineur si un deuxième signalement similaire n’était arrivé quarante minutes plus tard, décrivant exactement le même symptôme. Ce type de bug, rare mais sérieux, touche à la confiance des visiteurs et méritait un traitement immédiat plutôt qu’une mise en veille pour un prochain sprint.
Symptôme : une fuite de contenu personnalisé entre deux sessions
La reproduction en environnement de recette a confirmé le problème : dans certaines conditions, un visiteur pouvait recevoir une version du panier mise en cache pour un autre visiteur, avec un contenu différent du sien. Le site utilisait un cache de page FastCGI configuré pour exclure normalement les pages authentifiées et les pages de panier du cache, mais un changement récent dans la configuration Nginx, destiné à améliorer la performance générale du site, avait introduit une régression sur ce point précis.
Diagnostic : la clé de cache ne tenait pas compte de tout ce qui variait
Le cache FastCGI de Nginx construit sa clé de cache à partir d’une combinaison de paramètres définis par la directive fastcgi_cache_key. La configuration récente avait ajouté une prise en charge de la compression Brotli conditionnelle, en modifiant la clé de cache pour inclure l’en-tête Accept-Encoding, mais avait, par erreur de copier-coller lors de cette modification, supprimé du même mouvement le fragment de clé qui intégrait auparavant le cookie de session du panier.

# Configuration avant la régression (correcte)
fastcgi_cache_key "$scheme$request_method$host$request_uri$cookie_woocommerce_cart_hash";
# Configuration après le changement récent (régression introduite)
fastcgi_cache_key "$scheme$request_method$host$request_uri$http_accept_encoding";
# Le cookie de panier a disparu de la clé : deux paniers différents
# peuvent désormais partager la même entrée de cache si l'Accept-Encoding
# de leur navigateur est identique, ce qui est le cas la quasi-totalité du temps.
Concrètement, deux visiteurs anonymes avec des paniers différents, mais un navigateur envoyant le même en-tête Accept-Encoding (ce qui concerne la quasi-totalité des visiteurs utilisant un navigateur récent), généraient désormais la même clé de cache pour la page panier, ce qui signifie que le second visiteur à charger la page recevait la version mise en cache pour le premier, avec son contenu de panier à lui.
Pourquoi ce bug n’était pas immédiatement visible
La page panier de ce site restait, par une autre règle de configuration, exclue du cache pour les utilisateurs identifiés (connectés à un compte), ce qui limitait le bug aux visiteurs anonymes avec panier, une population plus restreinte mais bien réelle sur un site qui autorise l’achat sans création de compte. Le bug ne se manifestait par ailleurs que dans une fenêtre de quelques secondes après l’expiration naturelle du cache (TTL d’une minute sur cette page), le temps qu’une nouvelle entrée soit régénérée et remplace la précédente, ce qui expliquait sa rareté apparente et sa difficulté à être reproduit de façon systématique.
Le correctif immédiat
Deux actions ont été menées dans l’heure suivant la confirmation du diagnostic : une purge complète du cache concernant les pages de panier et de commande, et la restauration de la clé de cache correcte, en conservant cette fois la prise en compte de l’Accept-Encoding à côté du cookie de panier plutôt qu’à sa place.
fastcgi_cache_key "$scheme$request_method$host$request_uri$cookie_woocommerce_cart_hash$http_accept_encoding";
Le correctif de fond : exclure structurellement ces pages du cache
Au-delà de la restauration de la clé correcte, l’équipe a revu l’architecture pour rendre ce type de régression moins probable à l’avenir : plutôt que de compter sur une clé de cache toujours parfaitement complète (un pari fragile, comme cet incident l’a montré), les pages de panier et de commande ont été explicitement exclues de toute mise en cache FastCGI via une directive fastcgi_cache_bypass conditionnée sur l’URI, indépendamment de la composition de la clé.
set $bypass_cache 0;
if ($request_uri ~* "^/(panier|commande)") {
set $bypass_cache 1;
}
fastcgi_cache_bypass $bypass_cache;
fastcgi_no_cache $bypass_cache;
Communication envers les visiteurs concernés
Sur la base des journaux d’accès Nginx conservés, l’équipe a pu estimer la fenêtre d’exposition à quarante minutes et identifier un nombre limité de sessions potentiellement concernées, sans toutefois pouvoir garantir une liste exhaustive certaine ; une communication transparente a été envoyée par prudence aux clients ayant passé commande dans cette fenêtre, avec une vérification manuelle de leur commande.
- Ne jamais construire une clé de cache sans lister explicitement tout ce qui doit y figurer.
- Exclure structurellement les pages sensibles du cache plutôt que de compter sur une clé toujours exhaustive.
- Conserver des journaux d’accès suffisants pour estimer l’ampleur d’un incident après coup.
Un changement de configuration cache qui « améliore la performance » mérite exactement la même rigueur de relecture qu’un changement de code applicatif : les deux peuvent, de la même façon, mélanger ce qui ne doit jamais l’être.
Hors périmètre
Cet article ne traite pas la configuration standard d’un cache de page, sujet déjà couvert par ailleurs ; il se concentre spécifiquement sur ce cas de régression rare et sur la méthode de diagnostic qui a permis de le circonscrire rapidement.