« Pourquoi mon rendez-vous de 14h avec le docteur Petit apparaît sur la page du docteur Martin ? » — ce signalement, remonté par la secrétaire d’un cabinet regroupant trois praticiens, a déclenché une investigation rapide sur un module de prise de rendez-vous en ligne intégré au site WordPress du cabinet.
Le module affichait, pour chaque praticien, une page dédiée présentant ses créneaux disponibles via un identifiant en paramètre d’URL (/rendez-vous/?praticien=2). La logique métier elle-même, côté plugin de prise de rendez-vous, fonctionnait correctement : c’est la couche de cache de page placée devant WordPress qui mélangeait les réponses.
Le mécanisme du cache de page qui ignore un paramètre
La configuration du cache de page, un plugin de cache full-page classique couplé à une règle Nginx, générait sa clé de cache uniquement à partir du chemin d’URL, sans tenir compte des paramètres de requête (query string). Concrètement, /rendez-vous/?praticien=1 et /rendez-vous/?praticien=2 étaient traités comme la même ressource cachée, sous la même clé /rendez-vous/.
Le premier visiteur à consulter la page — peu importe quel praticien il avait demandé — figeait la réponse en cache. Tous les visiteurs suivants, pendant la durée du TTL, recevaient exactement le même planning, indépendamment du paramètre praticien qu’ils avaient réellement demandé dans leur URL.
Pourquoi ce type d’erreur passe inaperçu longtemps

Le bug ne se manifeste que lorsque deux visiteurs consultent des praticiens différents dans la même fenêtre de TTL, sur un cabinet à faible trafic ce cas de figure reste rare, ce qui explique qu’il soit resté invisible pendant plusieurs semaines avant qu’un chevauchement malheureux ne produise un signalement. Un test isolé, effectué par une seule personne consultant une seule page, ne révèle jamais ce type de défaut d’isolation.
Le correctif : une clé de cache qui intègre le paramètre
La règle Nginx de cache a été modifiée pour inclure le paramètre praticien dans la construction de la clé de cache, plutôt que de se limiter à l’URI :
fastcgi_cache_key "$scheme$request_method$host$request_uri";
Ce réglage, en apparence correct puisqu’il inclut $request_uri (qui contient normalement la chaîne de requête), se heurtait en réalité à une normalisation d’URL effectuée en amont par une règle de réécriture qui retirait certains paramètres jugés « non significatifs » pour d’autres pages du site, mais qui incluait par erreur praticien dans cette liste de nettoyage. Le correctif a consisté à exclure explicitement ce paramètre de la normalisation, en le traitant comme signifiant pour la clé de cache :
# Extrait de la normalisation de query string, avant correction :
# tous les paramètres hors liste blanche étaient retirés avant mise en cache
set $cache_key_praticien "";
if ($arg_praticien) {
set $cache_key_praticien "praticien=$arg_praticien";
}
fastcgi_cache_key "$scheme$host$uri$cache_key_praticien";
Vérifier l’isolation après correction
La vérification a consisté à ouvrir trois sessions de navigation distinctes (une par praticien), à rafraîchir chaque page plusieurs fois, et à confirmer via l’en-tête X-Cache-Status exposé par la configuration Nginx que chaque URL produisait bien une entrée de cache distincte (MISS puis HIT stable par praticien), sans contamination croisée.
- Vérifier systématiquement, pour toute page paramétrée par un identifiant métier, que ce paramètre entre bien dans la clé de cache et n’est pas neutralisé par une règle de normalisation générale.
- Tester l’isolation avec au moins deux identifiants différents consultés en parallèle, jamais un seul praticien à la fois.
- Documenter dans la configuration du cache la liste des paramètres significatifs, pour éviter qu’une future règle de nettoyage générique ne les efface à nouveau.
Un point distinct du RGPD, mais qui s’en approche
Ce billet ne traite pas des obligations réglementaires propres aux données de santé, qui relèvent d’un travail séparé sur l’hébergement et le traitement des données personnelles. Il documente uniquement le défaut technique de mise en cache qui a permis, temporairement, l’affichage croisé d’informations de planning entre deux praticiens — un rappel que la performance et la confidentialité des données ne sont jamais deux sujets complètement indépendants dès qu’une couche de cache s’intercale entre WordPress et le visiteur.
Toute page qui varie son contenu selon un paramètre doit faire varier sa clé de cache selon ce même paramètre, sans exception ni raccourci de configuration.
En résumé
L’incident est resté limité à un affichage erroné de créneaux entre trois praticiens du même cabinet, corrigé en quelques heures une fois la cause identifiée. Il illustre un piège classique des configurations de cache de page génériques appliquées sans revue spécifique aux pages dynamiques par paramètre : la performance gagnée par la mise en cache ne vaut rien si la clé de cache ne reflète pas fidèlement ce que la page affiche réellement.