Mise en cache WordPress qui ne casse pas les paniers et formulaires

Cet article vous a aidé ?

Tout va bien jusqu’à ce que ça ne le soit plus : le PDG essaie d’acheter un sweat, le total du panier est faux, et votre « cache haute performance » sert fièrement la session d’hier au client d’aujourd’hui. Ou votre formulaire de génération de leads s’envoie, « ça marche sur ma machine », puis disparaît dans une boucle de page de remerciement cachée en production.

Le cache n’est pas le méchant. Le cache mal ciblé l’est. L’objectif est la vitesse sans bugs fonctionnels : les paniers restent personnels, les paiements restent en temps réel, les formulaires restent uniques et les sessions authentifiées ne deviennent pas une diffusion publique.

Un modèle mental pratique : ce que vous pouvez mettre en cache en toute sécurité

La mise en cache WordPress est une pile, pas une fonctionnalité. Vous pouvez avoir (et vous avez souvent) plusieurs caches en même temps : cache navigateur, cache CDN en edge, cache reverse proxy (Varnish ou NGINX), cache d’opcode PHP, “page cache” WordPress via plugin, cache d’objets (Redis/Memcached) et caches de base de données. Chacun résout un problème différent, et chacun peut casser votre site à sa façon charmante.

Couches de cache et leurs usages

  • Cache navigateur (Cache-Control, ETag) : excellent pour les assets statiques (CSS/JS/images). Pas pour du HTML qui contient de la personnalisation.
  • Cache CDN : excellent pour les assets statiques et parfois le HTML anonyme si vous savez contourner correctement pour les sessions et les flux de paiement.
  • Reverse proxy / cache de page complète : excellent pour le trafic anonyme. Dangereux pour tout ce qui est spécifique à une session sauf si la logique des cookies est correctement gérée.
  • Plugin de cache de pages WordPress : écrit souvent du HTML statique sur disque. Facile à déployer, facile à mal configurer, peut entrer en conflit avec les caches proxy/CDN.
  • Cache d’objets (Redis) : met en cache les résultats de requêtes et objets calculés. Généralement sûr pour les utilisateurs connectés et WooCommerce si le plugin se comporte bien.
  • OPcache PHP : accélère l’exécution PHP. Rarement source de problèmes fonctionnels ; casse surtout les déploiements si vous oubliez d’invalider.

La règle qui vous évite des ennuis

Mettez en cache les GET anonymes pour les pages qui ne varient pas selon l’utilisateur, l’emplacement, la devise, le panier ou l’état d’authentification. Tout le reste doit être contourné (ou mis en cache avec des clés de variation explicites que vous pouvez défendre en cas d’incident).

C’est tout. Ça paraît évident. En pratique ça échoue parce que « anonyme » est glissant : un utilisateur peut être techniquement non connecté mais avoir un panier, un sélecteur de devise ou un nonce de formulaire qui doit être unique. De plus, WordPress adore les cookies. WooCommerce adore les cookies. Votre suite marketing adore les cookies. Les cookies sont la façon dont votre cache apprend à ne plus être un cache.

Une vérité sèche : si vous ne pouvez pas expliquer pourquoi une réponse est mise en cache en pointant vers des en-têtes, des clés et des règles de contournement, vous n’avez pas de cache — vous jouez à la roulette.

Petite blague #1 : si votre page de paiement est cacheable, félicitations — vous avez inventé le shopping communautaire.

Ce qui ne doit jamais être mis en cache (HTML)

Voici les éléments non négociables pour la plupart des sites WordPress :

  • /wp-admin/ et /wp-login.php
  • Toute page pour les utilisateurs connectés (sauf si vous avez un « cache privé » délibéré avec des clés par utilisateur, ce qui est rare et délicat)
  • WooCommerce : /cart/, /checkout/, /my-account/, endpoints add-to-cart, endpoints de fragments
  • Endpoints de formulaires et pages avec nonces expirants si vous comptez sur le comportement par défaut des nonces WordPress
  • Tout ce qui inclut des tokens CSRF, des prix personnalisés, des stocks devant être à jour, ou des messages dépendant de la session

Ce que vous pouvez généralement mettre en cache (HTML)

  • Pages marketing publiques, articles de blog, pages de documentation
  • Archives de catégories/tags pour les visiteurs anonymes
  • Pages de listing de produits (PLP) pour visiteurs anonymes, si prix/devise ne varient pas
  • Pages produit (PDP) pour visiteurs anonymes, en faisant attention aux messages « en stock »

Définissez « varier » sérieusement

Si le HTML change en fonction de l’un de ces éléments, votre cache doit soit contourner soit varier la clé de cache :

  • Cookies (panier, session, consentement, tests A/B)
  • Géolocalisation/pays (taxes, éligibilité livraison, règles de contenu)
  • Devise et langue
  • Type d’appareil (rarement pertinent ; le design responsive gagne généralement)
  • En-têtes d’autorisation

Faits intéressants et contexte historique (parce que le passé vous facture toujours)

  1. La mise en cache HTTP précède WordPress de plusieurs années. RFC 2616 (HTTP/1.1) a formalisé les sémantiques Cache-Control en 1999, et on discute encore de « must-revalidate » aujourd’hui.
  2. WordPress a popularisé tôt les « page cache plugins » parce que PHP était lent et l’hébergement mutualisé bon marché. Écrire du HTML statique sur disque était une tactique de survie, pas un choix esthétique.
  3. Le panier de WooCommerce est piloté par des cookies pour les invités. C’est pourquoi « non connecté » ne veut pas dire « sûr à mettre en cache ».
  4. ESI (Edge Side Includes) était une tentative précoce pour mettre en cache des pages mixtes dynamiques. Ça fonctionne, mais c’est lourd opérationnellement ; la plupart des stacks WordPress l’évitent sauf si elles ont une vraie équipe plateforme.
  5. Varnish a connu la gloire car il a fait du cache HTTP un reverse proxy de premier ordre. Son langage VCL est puissant, et aussi un excellent moyen de créer des règles de contournement difficiles à déboguer.
  6. Les cache stampedes ont été reconnus comme problème de fiabilité bien avant WordPress. Des techniques comme le request coalescing et « stale-while-revalidate » existent parce que les pics de trafic punissent les origines.
  7. Les navigateurs sont devenus plus stricts sur les cookies avec le temps. Les valeurs par défaut SameSite ont changé le comportement, ce qui peut modifier l’interaction des cookies de session avec les couches de cache.
  8. Beaucoup de CDNs n’activent « cache everything » que si vous le demandez explicitement. Quand on l’active pour du HTML, on oublie souvent la logique de contournement pour le panier et le checkout, puis on blâme WooCommerce.

Comment le cache casse les paniers et formulaires : modes de défaillance réels

1) Du HTML cache fuit du contenu spécifique à une session

Le classique : le widget panier affiche les articles d’un autre utilisateur, ou l’en-tête dit « Bonjour, Alex » à un inconnu. Cela arrive quand les clés de cache de page complète ne varient pas selon les bons cookies et que vous mettez en cache une réponse contenant des fragments personnalisés.

Même si la page principale du panier est exclue, le point d’accès de fragment du panier ou le mini-panier dans l’en-tête peut être mis en cache incorrectement par un plugin, un CDN ou une règle reverse proxy trop zélée.

2) Totaux de checkout obsolètes

Les taxes et la livraison peuvent varier selon l’adresse, le pays ou même le code postal. Si vous mettez en cache le HTML du checkout ou des réponses XHR utilisées pour calculer les totaux, vous pouvez servir des totaux erronés. Meilleur cas : la passerelle de paiement refuse. Pire cas : vous sous-facturez et vous apprenez la finance le week-end.

3) Les formulaires échouent parce que les nonces expirent ou sont partagés

Les nonces WordPress sont des jetons basés sur le temps. Beaucoup de plugins de formulaire les intègrent dans le HTML. Mettre en cache le HTML trop longtemps et le nonce expire, produisant des erreurs qui ressemblent à des messages « Contrôle de sécurité échoué » aléatoires. Le mettre en cache incorrectement entre utilisateurs peut créer des comportements de type replay où les soumissions vont vers le mauvais état.

4) Pages connectées sont mises en cache comme anonymes (ou inversement)

Si votre couche de cache n’examine pas des cookies comme wordpress_logged_in_* ou un cookie d’auth personnalisée, vous pouvez mettre en cache des pages connectées publiquement. Ou vous pouvez contourner le cache pour tout le monde parce que vous avez défini un cookie large comme « consent=true » et votre cache considère tout cookie comme signal de contournement. Les deux sont mauvais. L’un est un incident de sécurité ; l’autre est un incident de budget.

5) « Optimisation » conflictuelle : plugin cache vs proxy vs CDN

Plusieurs caches peuvent coopérer, mais seulement si vous décidez qui est l’autorité pour le HTML. Quand un plugin de cache définit des en-têtes d’une façon, NGINX les écrase, et le CDN les ignore, votre débogage devient une danse interprétative.

6) Purge storms et troupeaux tonnants

Purger tout le cache à chaque mise à jour de produit est un défaut courant. Sur des boutiques chargées, cela devient une attaque par déni de service auto-infligée : caches froids, origine saturée, file PHP-FPM qui grandit, puis timeouts, puis retries, puis plus de charge.

Il y a une idée paraphrasée utile à garder sur un post-it de Werner Vogels (CTO d’Amazon) : idée paraphrasée : construire des systèmes en supposant que les choses vont échouer, et concevoir pour la récupération plutôt que la perfection.

Playbook de diagnostic rapide : trouver le goulot et le bug vite

Voici le playbook quand quelqu’un dit « le panier est faux » ou « les formulaires ne s’envoient pas » et que vous avez besoin d’un signal rapide.

Première étape : confirmez si vous servez du HTML mis en cache

  1. Vérifiez les en-têtes de réponse pour hits/misses et Cache-Control.
  2. Comparez anonyme vs avec cookie panier (ou cookie connecté) pour voir si le cache varie correctement.
  3. Vérifiez CDN vs origine : l’edge sert-elle une réponse mise en cache même si l’origine dit no-store ?

Deuxième étape : isolez la couche qui met en cache

  1. Contournez le CDN (hôte d’origine ou IP interne) et testez à nouveau.
  2. Contournez le reverse proxy (tapez directement le PHP upstream si possible) et testez à nouveau.
  3. Désactivez temporairement le plugin de page cache (ou mettez le site en « mode développement » si votre plugin le permet) et testez à nouveau.

Troisième étape : validez les endpoints spécifiques WooCommerce et formulaires

  1. Vérifiez que /cart/, /checkout/ et les endpoints de fragments WooCommerce ne sont pas en cache.
  2. Vérifiez que les requêtes POST ne sont jamais mises en cache (elles ne devraient pas l’être, mais ne faites pas confiance aux valeurs par défaut).
  3. Pour les formulaires, vérifiez la fraîcheur des nonces et que la page contenant le nonce n’est pas mise en cache au-delà de son TTL.

Quatrième étape : vérifiez la pression sur l’origine

  1. Si vous voyez des misses de cache, assurez-vous que l’origine peut gérer le taux de misses : file PHP-FPM, latence DB, santé Redis.
  2. Si vous voyez des hits de cache mais du contenu erroné, concentrez-vous sur la variation de clé et les règles de contournement, pas sur « plus de matériel ».

Tâches pratiques : commandes, sortie attendue et ce qu’il faut décider

Ces tâches sont conçues pour un hôte Linux typique exécutant NGINX + PHP-FPM, éventuellement Varnish et Redis, en frontal d’un CDN. Adaptez les chemins et noms de services à votre stack. Chaque tâche inclut (a) une commande, (b) ce que signifie la sortie, et (c) la décision à prendre.

Task 1: Inspecter les en-têtes de cache pour une page publique

cr0x@server:~$ curl -sI https://store.example.com/ | egrep -i 'cache-control|age|expires|etag|x-cache|via|cf-cache-status|server'
server: nginx
cache-control: public, max-age=600
etag: "a1b2c3"
x-cache: HIT
age: 87
via: 1.1 varnish

Ce que cela signifie : Vous voyez une mise en cache explicite (public, max-age=600), et un hit du reverse proxy (x-cache: HIT) avec l’Age qui augmente. Bien pour la page d’accueil si elle est sûre en anonyme.

Décision : Si c’est une page marketing : conservez-la. Si la page d’accueil inclut des totaux de panier personnalisés ou « récemment vus » pour les invités : vous devez rendre ces widgets côté client ou contourner le cache pour les utilisateurs avec les cookies concernés.

Task 2: Inspecter les en-têtes de cache pour les pages panier et checkout

cr0x@server:~$ curl -sI https://store.example.com/cart/ | egrep -i 'cache-control|age|x-cache|cf-cache-status|set-cookie'
cache-control: no-store, no-cache, must-revalidate, max-age=0
x-cache: MISS
set-cookie: woocommerce_items_in_cart=1; path=/; secure; HttpOnly

Ce que cela signifie : Le panier est correctement marqué non-cacheable et provoque un miss (ce que vous voulez). Le cookie indique l’état session/panier.

Décision : Si vous voyez public ou HIT ici, considérez cela comme un bug de production : ajoutez des règles de contournement pour ces chemins à chaque couche de cache.

Task 3: Vérifier la variation quand des cookies sont présents

cr0x@server:~$ curl -sI https://store.example.com/ -H 'Cookie: woocommerce_items_in_cart=1; wp_woocommerce_session_123=abc' | egrep -i 'cache-control|x-cache|age|vary'
cache-control: private, no-store, max-age=0
x-cache: BYPASS
vary: Accept-Encoding

Ce que cela signifie : Avec des cookies de panier, le cache est contourné et la réponse est privée/no-store. C’est une base saine.

Décision : Si cela indique toujours x-cache: HIT, votre clé de cache ignore les cookies ou votre logique de contournement ne se déclenche pas. Corrigez avant d’« optimiser » quoi que ce soit d’autre.

Task 4: Confirmer le comportement du CDN vs origine (connexion directe à l’origine)

cr0x@server:~$ curl -sI https://origin.store.example.com/checkout/ | egrep -i 'cache-control|x-cache|age|server'
server: nginx
cache-control: no-store, no-cache, must-revalidate, max-age=0
x-cache: MISS

Ce que cela signifie : L’origine ne met pas en cache le checkout. Si le nom public renvoie encore un checkout en cache, le CDN en est le coupable.

Décision : Ajoutez des règles de contournement CDN pour les chemins checkout/cart/account et pour les cookies pertinents. Ne comptez pas sur « respecter les en-têtes de l’origine » sauf si vous l’avez vérifié en pratique.

Task 5: Trouver quel plugin de cache est actif (WordPress CLI)

cr0x@server:~$ cd /var/www/store
cr0x@server:~$ sudo -u www-data wp plugin list --status=active
+--------------------------+--------+-----------+---------+
| name                     | status | update    | version |
+--------------------------+--------+-----------+---------+
| woocommerce              | active | available | 8.6.1   |
| redis-cache              | active | none      | 2.5.3   |
| wp-super-cache           | active | none      | 1.9.4   |
+--------------------------+--------+-----------+---------+

Ce que cela signifie : Vous avez un plugin de cache de pages (wp-super-cache) et un plugin de cache d’objets (redis-cache) actifs.

Décision : Décidez qui est responsable du cache de page complète : le plugin ou le reverse proxy. Les faire fonctionner ensemble est possible mais crée souvent des incidents « pourquoi ceci est en cache ? ». Choisissez l’un et configurez l’autre pour rester dans son rôle.

Task 6: Vérifier si le cache d’objets Redis est réellement utilisé

cr0x@server:~$ sudo -u www-data wp redis status
Status: Connected
Client: PhpRedis (v5.3.7)
Redis: 7.0.15
Drop-in: Valid

Ce que cela signifie : Le cache d’objets fonctionne. Cela réduit la charge sur la base de données et peut vous permettre de dépendre moins d’un cache HTML agressif pour les flux connectés.

Décision : Gardez le cache d’objets activé. Si le cache d’objets est déconnecté, corrigez cela avant de resserrer les TTL HTML ; sinon vous renvoyez la charge vers MySQL.

Task 7: Vérifier la présence du cookie de session WooCommerce lors de la navigation

cr0x@server:~$ curl -sI https://store.example.com/product/hoodie/ | egrep -i 'set-cookie|cache-control|x-cache'
cache-control: public, max-age=600
x-cache: HIT

Ce que cela signifie : La page produit est mise en cache et ne définit pas de cookies panier/session à la consultation. C’est idéal.

Décision : Si les pages produit définissent wp_woocommerce_session_* pour tout le monde, votre taux de cache s’effondrera. Investiguer les plugins qui démarrent des sessions sur chaque vue de page.

Task 8: Vérifier la configuration fastcgi_cache NGINX et la logique de contournement

cr0x@server:~$ sudo nginx -T 2>/dev/null | egrep -n 'fastcgi_cache|fastcgi_cache_bypass|fastcgi_no_cache|cache_key|set \$skip_cache' | head -n 40
123:    fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m max_size=5g;
181:    set $skip_cache 0;
186:    if ($request_method = POST) { set $skip_cache 1; }
190:    if ($request_uri ~* "/(cart|checkout|my-account)/") { set $skip_cache 1; }
194:    if ($http_cookie ~* "woocommerce_items_in_cart|wp_woocommerce_session_|wordpress_logged_in_") { set $skip_cache 1; }
221:    fastcgi_cache_bypass $skip_cache;
222:    fastcgi_no_cache $skip_cache;
223:    fastcgi_cache WORDPRESS;
224:    fastcgi_cache_key "$scheme$request_method$host$request_uri";

Ce que cela signifie : Cet hôte utilise fastcgi_cache NGINX. Il y a un contournement explicite pour POST, des chemins WooCommerce clés et des cookies clés.

Décision : Assurez-vous que la clé de cache inclut la query string quand c’est pertinent (voir plus loin). Assurez-vous de contourner sur les bons cookies pour votre site et évitez de contourner sur des cookies inoffensifs comme analytics ou consentement sauf si nécessaire.

Task 9: Tester si les query strings effondrent incorrectement les entrées du cache

cr0x@server:~$ curl -sI "https://store.example.com/?utm_source=test1" | egrep -i 'x-cache|age'
x-cache: HIT
age: 140

Ce que cela signifie : La page mise en cache n’a pas varié selon la query string, ce qui est généralement bon pour les paramètres de tracking. Mais cela peut être mauvais si votre site utilise des paramètres de requête fonctionnels (filtres, devise, langue).

Décision : Retirez les paramètres marketing connus en edge, mais variez le cache sur les paramètres fonctionnels (ex. ?currency=, ?lang=, filtres). Ne devinez pas — inventairez ce que votre thème et vos plugins utilisent.

Task 10: Vérifier le comportement Varnish et quels cookies provoquent un pass

cr0x@server:~$ curl -sI https://store.example.com/ -H 'Cookie: wordpress_logged_in_abc=1' | egrep -i 'x-cache|via|set-cookie|cache-control'
via: 1.1 varnish
x-cache: MISS
cache-control: private, no-store, max-age=0

Ce que cela signifie : Le cookie connecté déclenche un miss/contournement et une réponse privée.

Décision : Confirmez que Varnish ne met pas en cache les pages authentifiées. Si vous voyez HIT ici, arrêtez et corrigez le VCL ; vous risquez une fuite de contenu privé.

Task 11: Vérifier la pression PHP-FPM pendant les misses de cache

cr0x@server:~$ sudo ss -lntp | egrep 'php-fpm|:9000'
LISTEN 0      4096         127.0.0.1:9000       0.0.0.0:*    users:(("php-fpm8.2",pid=1211,fd=9))

Ce que cela signifie : PHP-FPM écoute localement. C’est normal.

Décision : Ensuite, vérifiez le statut du pool et l’arriéré. Si l’arriéré augmente pendant le trafic, vos règles de contournement peuvent être trop larges ou votre TTL trop court.

Task 12: Inspecter la page de statut PHP-FPM (si activée)

cr0x@server:~$ curl -s http://127.0.0.1/php-fpm-status | egrep -i 'listen queue|idle processes|active processes|max children reached'
listen queue:         0
idle processes:       12
active processes:     3
max children reached: 0

Ce que cela signifie : Pas de queue, beaucoup de workers inactifs. L’origine est saine.

Décision : Si listen queue grimpe et que max children reached augmente, vous avez un souci de capacité d’origine. Augmentez la capacité FPM, réduisez le taux de misses, ou les deux.

Task 13: Vérifier rapidement la latence MySQL

cr0x@server:~$ mysqladmin -uroot -p ping; mysqladmin -uroot -p status
mysqld is alive
Uptime: 183204  Threads: 42  Questions: 23801984  Slow queries: 17  Opens: 231  Flush tables: 1  Open tables: 1024  Queries per second avg: 129.9

Ce que cela signifie : MySQL est en ligne ; des requêtes lentes existent mais ne semblent pas exploser sur cet instantané.

Décision : Si les requêtes lentes grimpent lors des purges de cache, réduisez le périmètre de purge, ajoutez un cache d’objets ou optimisez les index/requêtes DB. Ne « corrigez » pas cela en mettant en cache le checkout.

Task 14: Vérifier que les POST ne sont pas mis en cache par un intermédiaire

cr0x@server:~$ curl -s -o /dev/null -D - -X POST https://store.example.com/wp-admin/admin-ajax.php | egrep -i 'cache-control|x-cache|status|via'
HTTP/2 400
cache-control: no-store, no-cache, must-revalidate, max-age=0
via: 1.1 varnish
x-cache: MISS

Ce que cela signifie : La réponse POST n’est pas mise en cache (et vous avez eu un 400 parce qu’il n’y avait pas de payload). C’est correct pour ce test.

Décision : Si vous voyez un cache HIT sur un POST, vous avez une mauvaise configuration sérieuse de proxy/CDN. Corrigez immédiatement ; mettre en cache les POST casse plus que les paniers — ça casse la réalité.

Task 15: Vérifier les cron WordPress et jobs en arrière-plan (charge de purge)

cr0x@server:~$ sudo -u www-data wp cron event list --fields=hook,next_run,recurrence | head
+------------------------------+---------------------+------------+
| hook                         | next_run            | recurrence |
+------------------------------+---------------------+------------+
| wp_version_check             | 2025-12-27 03:12:00 | twice_daily|
| woocommerce_cleanup_sessions | 2025-12-27 02:45:00 | daily      |
| wp_scheduled_delete          | 2025-12-27 02:10:00 | daily      |
+------------------------------+---------------------+------------+

Ce que cela signifie : Le nettoyage des sessions WooCommerce et d’autres tâches cron s’exécutent régulièrement. Certains plugins de cache accrochent des tâches cron pour purge/preload.

Décision : Si vous voyez des jobs de purge/preload trop fréquents, limitez-les. Le préchargement peut devenir un crawler auto-infligé qui vole la capacité des vrais utilisateurs.

Patrons de configuration qui fonctionnent : plugin, serveur, CDN et cookies

Décidez qui met en cache le HTML

Choisissez un cache HTML principal. Mon défaut opinionné pour la production :

  • Reverse proxy cache (fastcgi_cache NGINX ou Varnish) pour le HTML anonyme.
  • CDN pour les assets statiques, éventuellement le HTML anonyme si vous avez des règles de contournement disciplinées.
  • Cache d’objets (Redis) toujours, pour anonyme et connecté.
  • Plugin de page cache WordPress seulement si vous ne contrôlez pas la couche serveur. Si vous contrôlez le serveur, gardez les plugins minimaux.

Plus il y a d’endroits où vous pouvez accidentellement mettre en cache une page de paiement, plus il est probable qu’un jour vous la mettrez en cache.

Contournement basé sur les cookies : le cœur de la sécurité WooCommerce

WooCommerce définit des cookies qui signalent l’état panier/session. Les noms varient, mais vous verrez couramment :

  • woocommerce_items_in_cart
  • woocommerce_cart_hash
  • wp_woocommerce_session_*
  • wordpress_logged_in_* (pour l’auth WordPress)

Au niveau du reverse proxy ou du cache NGINX, votre règle de contournement devrait typiquement se déclencher quand l’un de ces cookies est présent. Cela limite le cache HTML à une navigation vraiment anonyme.

Exclusions basées sur le chemin : toujours nécessaires

Même avec un contournement par cookie, excluez explicitement les chemins sensibles. Parce que les humains testeront le checkout sans panier, sans cookies, et déclareront que c’est « sûr ». Puis un vrai acheteur arrive avec de l’état et le cache fait quelque chose de créatif.

Exclusions courantes :

  • /cart/, /checkout/, /my-account/
  • /?wc-ajax=* et endpoints AJAX WooCommerce
  • /wp-admin/, /wp-login.php
  • Endpoints de formulaires, surtout si vous utilisez admin-ajax ou des routes REST pour les soumissions

Gardez le « vary » intentionnel (et restreint)

Si vous variez par trop de choses, vous détruisez le taux de hit du cache et vous vous demandez pourquoi la performance n’a pas augmenté. Si vous variez par trop peu, vous fuyez des données et cassez des flux.

Bons motifs pour varier :

  • Accept-Encoding (géré automatiquement)
  • Pays si votre site change taxes/livraison/contenu légal
  • Devise si les prix changent
  • Langue si le contenu change

Mauvais motifs pour varier :

  • Cookies marketing aléatoires
  • État de consentement (sauf si cela change substantiellement le HTML)
  • User agent (sauf si vous servez un HTML complètement différent)

Utilisez des stratégies « stale » pour prévenir les stampedes

Si votre reverse proxy le permet, autorisez la distribution de contenu périmé pour les pages anonymes pendant la revalidation en arrière-plan. L’utilisateur obtient une réponse rapide ; l’origine n’est pas assaillie quand une page populaire expire.

Mais n’appliquez pas le contenu périmé aux endpoints transactionnels. Personne ne veut de checkout périmé.

Formulaires : traitez les nonces comme du lait, pas du miel

Quand un formulaire inclut un nonce ou token CSRF dans le HTML, vous avez trois options viables :

  1. Ne pas mettre en cache la page du formulaire (simple, sûr, peut coûter en performance).
  2. Mettre la page en cache mais rendre le nonce dynamiquement via AJAX ou edge-side includes (plus complexe, évolutif).
  3. TTL court pour la page du formulaire, et accepter que certains onglets laissés ouverts échouent (décision métier ; logguez et surveillez les erreurs).

Petite blague #2 : un nonce mis en cache, c’est comme une carte magnétique d’hôtel photocopiée — techniquement une carte, pratiquement une plainte.

CDN : mettez le HTML en cache seulement si vous pouvez contourner précisément

Les CDN excellent pour les assets statiques. Pour le HTML, avancez seulement si :

  • Vous pouvez contourner selon les cookies et les chemins.
  • Vous pouvez purger sélectivement (par URL ou tag) plutôt que « purger tout ».
  • Vous pouvez voir des en-têtes de debug (statut cache, clé de cache ou variation) pendant les incidents.

Si votre règle CDN est « cachez tout » avec TTL d’une heure et un contournement vague, vous finirez par mettre en cache quelque chose que vous ne vouliez pas. Ce n’est pas du cynisme. C’est le temps.

Le cache d’objets n’est pas un substitut au cache de pages (et c’est une bonne chose)

Redis accélère l’exécution WordPress sans servir le HTML d’un utilisateur à un autre. C’est généralement le gain de performance le moins risqué à déployer sur des sites lourds WooCommerce, car il ne court-circuite pas la logique requête/réponse au niveau HTTP.

Utilisez-le pour réduire la tentation de mettre en cache du HTML dynamique.

Trois mini-histoires d’entreprise issues des tranchées du cache

Mini-histoire 1 : L’incident causé par une mauvaise hypothèse

L’entreprise : un détaillant de taille moyenne avec une stack WordPress « modernisée ». Nouveau reverse proxy cache devant PHP-FPM. Une démo de sprint montrait la page d’accueil en dessous de 100ms. Tout le monde a applaudi. Quelqu’un a dit « la performance est en gros résolue ».

L’hypothèse erronée était simple : « Si l’utilisateur n’est pas connecté, la réponse est sûre à mettre en cache. » C’est une phrase qui sonne ingénierie jusqu’à ce qu’on se rappelle de l’existence de WooCommerce.

Le lundi, le support client a signalé des acheteurs voyant « des articles déjà dans votre panier » à la première visite. Quelques partages d’écran plus tard, c’était pire : le mini-panier déroulant montrait des produits appartenant à d’autres sessions. Pas de données de paiement, pas d’adresses — mais quand même une fuite de confidentialité et un coup porté à la crédibilité.

Le débogage a montré que le reverse proxy ignorait totalement les cookies WooCommerce. Les invités étaient « anonymes », mais leur état de panier était suivi par wp_woocommerce_session_*. La clé de cache n’était que host + uri. La première personne à ajouter un produit « empoisonnait » le HTML mis en cache pour tous ceux qui tapaient ensuite cette page.

La correction a été ennuyeuse : contourner le cache si les cookies de session/panier WooCommerce sont présents, et exclure explicitement les chemins cart/checkout. Le suivi a été plus important : ils ont rédigé une page « qu’est-ce qui rend une réponse spécifique à un utilisateur » et l’ont intégrée à toute revue de changement liée au cache.

Mini-histoire 2 : L’optimisation qui a mal tourné

L’entreprise : service par abonnement avec beaucoup de marketing de contenu et un checkout WooCommerce pour les add-ons. Ils voulaient augmenter le taux de hit au CDN, alors ils ont décidé de « normaliser » les cookies : stripper la plupart des cookies en edge pour rendre plus de requêtes cacheables.

L’idée paraissait intelligente. Elle était aussi incomplète. Un des cookies supprimés était un cookie sélecteur de devise défini par un plugin. Un autre était un cookie de sélection de pays utilisé pour l’affichage des taxes. Ils n’ont pas varié le cache par pays parce que « l’en-tête geo du CDN devrait le gérer », sauf qu’ils ne l’ont pas connecté à la clé de cache. Et le CDN, étant une machine, a fait exactement ce qu’on lui a dit.

Résultat : les visiteurs canadiens voyaient les prix US sur les pages produit. Certains allaient en checkout et voyaient les totaux basculer. D’autres voyaient un message « taxe incluse » qui ne s’appliquait pas. Les conversions ont chuté et les tickets support ont explosé. L’incident a été classé « régression suite à un changement de performance », langage corporate pour « on a cassé l’argent en poursuivant des millisecondes ».

Le plan de recouvrement a été discipliné : rollback du stripping de cookies, puis réintroduction avec allowlists. Ils ont documenté quels query params et cookies étaient « fonctionnels » et devaient rester dans la variation du cache. Ils ont aussi ajouté des vérifications automatisées : un curl scripté qui compare les en-têtes et quelques pages représentatives avec et sans indicateurs de devise/pays.

Finalement ils ont fait marcher le caching HTML CDN pour les pages marketing anonymes uniquement, avec un contournement strict pour tout ce qui est commerce. Le taux de hit du cache était inférieur au rêve initial. Les revenus ont cessé de faire des choses bizarres. Échange équitable.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la situation

L’entreprise : SaaS B2B utilisant WordPress pour le marketing et la documentation, avec des formulaires liés à leur CRM. Ils avaient un reverse proxy et un CDN, et prévoyaient une campagne qui ferait monter le trafic.

Au lieu de « monter le cache », ils ont fait l’ennuyeuse chose : ils ont listé toutes les URL et endpoints qui ne doivent jamais être mis en cache, puis ils l’ont testé en staging avec des en-têtes de type production. Ils ont aussi créé un standard d’en-têtes debug cache : l’origine émettait X-Cache-Bypass-Reason lorsqu’elle contourne, et le reverse proxy émettait X-Cache: HIT/MISS/BYPASS.

Pendant la campagne, la performance était correcte — jusqu’à ce que des formulaires commencent à échouer pour un sous-ensemble d’utilisateurs. La réaction rapide aurait été de blâmer le plugin de formulaire ou le CRM. Au lieu de cela, ils ont suivi leur playbook : vérifier les en-têtes sur la page d’atterrissage du formulaire. Elle était mise en cache 30 minutes au CDN à cause d’un jeu de règles dépareillées déployé la veille.

Parce qu’ils avaient des en-têtes de debug cohérents, il a été évident en quelques minutes quelle couche était en faute. Ils ont ajusté la règle CDN pour contourner le cache pour les pages de formulaire et toute réponse définissant certains cookies. Les erreurs ont cessé. La campagne a continué. Personne n’a eu à inventer une histoire pour la direction sur des « problèmes intermittents tiers ».

Ils n’ont pas eu d’applaudissements pour la norme d’en-têtes. Ils ont obtenu mieux : le silence dans le canal d’incidents.

Erreurs courantes : symptôme → cause profonde → correctif

1) Le panier affiche les articles d’un autre utilisateur

Symptôme : Mini-panier ou page panier affiche des articles inattendus ; les utilisateurs signalent un « panier fantôme ».

Cause profonde : Le cache de page complète ne contourne pas sur les cookies session/panier WooCommerce, ou des endpoints de fragments sont mis en cache.

Correctif : Contourner le cache sur woocommerce_items_in_cart, woocommerce_cart_hash, wp_woocommerce_session_*. Exclure /cart/, /checkout/, /?wc-ajax=*. S’assurer que l’endpoint de fragments n’est pas mis en cache au CDN ou proxy.

2) Les totaux du checkout changent ou sont incorrects

Symptôme : Livraison/taxes changent entre les étapes, ou totaux en désaccord avec la passerelle de paiement.

Cause profonde : HTML du checkout en cache ou réponses AJAX utilisées pour calculer les totaux en cache ; variation manquante par pays/devise.

Correctif : Ne jamais mettre en cache le HTML du checkout. Contourner le cache pour les flux de sélection pays/devise. Pour les CDNs, désactiver la mise en cache des AJAX WooCommerce et endpoints checkout entièrement.

3) « Security check failed » sur les formulaires

Symptôme : Erreurs de validation nonce, échec intermittent de formulaires, surtout après qu’une page est restée ouverte.

Cause profonde : Page mise en cache contient un nonce expiré ; TTL trop long ; cache partagé entre utilisateurs.

Correctif : Exclure les pages de formulaires du cache ou générer les nonces dynamiquement. Raccourcir le TTL seulement si vous acceptez les échecs d’onglets laissés ouverts ; surveillez les taux d’erreur.

4) Les utilisateurs connectés voient des pages anonymes mises en cache (ou inversement)

Symptôme : Barre admin manquante, page de compte semble déconnectée, ou utilisateurs anonymes voient du contenu privé.

Cause profonde : Le cache ne respecte pas wordpress_logged_in_* ; en-tête Authorization ignorée ; clé de cache inconsistante.

Correctif : Contourner sur les cookies d’auth WordPress et l’en-tête Authorization. Validez avec curl en utilisant des cookies. Si vous avez besoin de cache pour pages connectées, implémentez une variation par utilisateur explicitement (rare ; testez intensivement).

5) Le taux de hit du cache s’effondre après l’ajout d’outils analytics/consentement

Symptôme : Soudain tout est MISS ; la charge origine explose.

Cause profonde : Le contournement du cache est déclenché par « n’importe quel cookie » ou par une regex large qui capture les cookies consent/analytics.

Correctif : Passez d’un « contourner si un cookie existe » à un modèle allowlist/denylist : contourner seulement sur les cookies fonctionnels (panier/session/auth). Supprimez les cookies non pertinents de la considération du cache quand c’est sûr.

6) Les purges causent des pannes ou timeouts

Symptôme : Le site ralentit après des mises à jour de contenu ; pics de 5xx ; CPU DB qui grimpe.

Cause profonde : Stratégie purge-all ; crawler de préchargement trop agressif ; cache stampede à l’expiration.

Correctif : Purgez sélectivement ; implémentez stale-while-revalidate si possible ; rate-limitez le préchargement ; assurez-vous que l’origine a la capacité pour les bursts de misses.

7) Les pages produit affichent la mauvaise langue ou devise

Symptôme : Les visiteurs voient une langue/devise qui ne correspond pas à leur sélection.

Cause profonde : CDN/proxy met en cache le HTML sans varier sur le cookie/en-tête devise/langue ; query params mal supprimés.

Correctif : Vary la clé de cache sur le sélecteur fonctionnel (cookie/en-tête/query param). Ou contournez le cache pour les pages où cela ne peut pas être fait proprement.

8) « Ça marche quand je contourne le cache »

Symptôme : Le bug disparaît avec le cache désactivé ; réapparaît quand le cache est activé.

Cause profonde : Le cache masque une dépendance d’état (nonce/session), ou met en cache une réponse d’erreur.

Correctif : Empêchez la mise en cache des statuts d’erreur ; assurez le contournement sur les cookies stateful ; resserrez Cache-Control pour les pages sensibles ; vérifiez couche par couche avec les en-têtes.

Checklists / plan étape par étape pour une mise en cache sûre

Étape 1 : Inventairez ce qui doit rester dynamique

  • Listez les URLs transactionnelles : cart, checkout, account, login, admin.
  • Listez les pages de formulaires et endpoints de soumission (admin-ajax, routes REST).
  • Listez les fonctionnalités de personnalisation sur pages anonymes (récemment vus, tarification geo, sélecteurs devise/langue).

Étape 2 : Choisissez votre autorité de cache

  • Si vous contrôlez NGINX/Varnish : utilisez-le pour le cache HTML et désactivez les plugins de page cache WordPress (ou configurez-les pour ne gérer que le cache navigateur/optimisation statique).
  • Si vous êtes sur un hébergement contraint : utilisez un plugin de cache réputé, et gardez le caching HTML CDN conservateur.

Étape 3 : Implémentez des exclusions strictes

  • Excluez /wp-admin/, /wp-login.php.
  • Excluez /cart/, /checkout/, /my-account/.
  • Excluez les endpoints AJAX WooCommerce (wc-ajax) et les fragments.
  • Excluez les endpoints de formulaires et pages avec nonces si vous ne pouvez pas les rendre dynamiques.

Étape 4 : Implémentez le contournement basé sur les cookies

  • Contournez sur wordpress_logged_in_* et tous les cookies d’auth/session que vous utilisez.
  • Contournez sur les cookies panier/session WooCommerce : woocommerce_items_in_cart, woocommerce_cart_hash, wp_woocommerce_session_*.
  • Ne contournez pas pour « n’importe quel cookie ». Utilisez un matching ciblé.

Étape 5 : Décidez des clés de variation pour langue/devise/geo

  • Si devise/langue modifient le HTML, variez la clé de cache ou contourez.
  • Supprimez les query params marketing de la clé ; conservez les paramètres fonctionnels.

Étape 6 : Fixez des TTL comme un adulte

  • Pages marketing/blog : 5–30 minutes est un point de départ courant.
  • Pages produit : TTL plus court si l’inventaire/prix change souvent ; sinon TTL modéré avec purge à la mise à jour.
  • Pages transactionnelles : no-store/no-cache.

Étape 7 : Implémentez une purge sélective

  • Purgez seulement les URLs modifiées lorsqu’un post/produit est mis à jour.
  • Évitez purge-all sauf déploiements ou urgences.
  • Rate-limitez les requêtes de purge pour protéger les caches et l’origine.

Étape 8 : Ajoutez de l’observabilité pour la mise en cache

  • Ajoutez X-Cache et, idéalement, X-Cache-Bypass-Reason au niveau proxy.
  • Suivez le ratio de hits cache, le temps de réponse origine et les taux 5xx.
  • Créez un test synthétique « canary » pour cart/checkout qui s’exécute toutes les quelques minutes et alerte si ces pages deviennent cacheables.

Étape 9 : Testez avec de vrais cookies et flux

  • Naviguez anonyme sans cookies.
  • Naviguez anonyme avec cookie panier présent.
  • Utilisateur connecté.
  • Variations pays/devise si vous les supportez.
  • Soumission de formulaire depuis une page laissée ouverte 30+ minutes (scénario d’expiration de nonce).

FAQ

1) Puis-je mettre en cache les pages WooCommerce du tout ?

Vous pouvez mettre en cache certaines pages WooCommerce pour les utilisateurs anonymes : les listings produits et les pages produit sont souvent OK. Ne mettez pas en cache le panier, le checkout, le compte ou les endpoints AJAX WooCommerce. La frontière de sécurité est l’état de session, pas « est-ce une page de boutique ».

2) Pourquoi mon taux de hit chute presque à zéro quand j’active WooCommerce ?

Généralement parce que quelque chose crée des cookies de session/panier trop tôt (sur les vues produit, la page d’accueil ou chaque page). Une fois qu’un cookie est présent, votre logique de contournement peut éviter la mise en cache pour cet utilisateur. Trouvez le plugin/thème qui démarre les sessions et arrêtez-le.

3) Dois-je contourner le cache si n’importe quel cookie existe ?

Non. C’est la voie rapide pour transformer votre cache en fichier de configuration décoratif. Contournez seulement pour les cookies fonctionnels (auth, panier, session, devise/langue si vous ne pouvez pas varier en sécurité).

4) Mon CDN dit qu’il « respecte les en-têtes d’origine ». Pourquoi met-il quand même en cache le checkout ?

Parce que « respecte » a des notes de bas de page. Vous pouvez avoir une règle de page qui l’emporte, ou le CDN peut traiter certains statuts différemment, ou vous cachez par défaut et ne contournez que sur des chemins oubliés. Vérifiez en comparant les en-têtes origine vs edge et en testant avec des cookies.

5) Quelle est la configuration la plus sûre pour WordPress + WooCommerce ?

Cache de page conservateur pour les pages anonymes uniquement (reverse proxy ou plugin), contournement strict pour panier/checkout/compte/auth et cookies WooCommerce, plus cache d’objets Redis. Cachez les assets statiques agressivement au CDN/navigateur.

6) Ai-je besoin de Varnish si j’ai déjà un plugin de cache WordPress ?

Pas nécessairement. Varnish peut être excellent, mais c’est un composant supplémentaire à gérer. Si vous contrôlez le serveur et voulez un comportement prévisible à grande échelle, Varnish ou fastcgi_cache NGINX est souvent plus propre qu’un plugin. Si vous ne contrôlez pas le serveur, un plugin peut être l’option pratique.

7) Pourquoi les formulaires ne cassent-ils que parfois ?

Parce que les défaillances de cache dépendent du temps : fenêtres d’expiration des nonces, TTL de cache, et si un utilisateur tape une copie mise en cache ou déclenche un rendu frais. Les échecs intermittents de formulaires sont un fort indice que du HTML avec nonce est mis en cache trop longtemps.

8) Le cache d’objets (Redis) est-il risqué pour WooCommerce ?

Généralement, c’est moins risqué que le cache de pages parce qu’il ne sert pas le HTML d’un utilisateur à un autre. Les principaux risques sont opérationnels : disponibilité de Redis, dimensionnement mémoire et comportement d’éviction. Surveillez-le comme toute dépendance.

9) Comment savoir quelle couche a servi la réponse mise en cache ?

Les en-têtes. Ajoutez-les si vous ne les avez pas. Les en-têtes de statut cache du CDN, X-Cache du reverse proxy et les en-têtes d’origine comme Cache-Control vous indiquent où chercher. Si vous ne pouvez pas le dire, vous ne pouvez pas déboguer sous pression.

10) Puis-je mettre en cache les utilisateurs connectés en toute sécurité ?

C’est possible, mais rarement rentable pour WordPress sauf si vous construisez des clés de cache par utilisateur et acceptez la complexité. Pour la plupart des sites, investissez dans le cache d’objets, des requêtes efficaces et le tuning PHP-FPM. Gardez le HTML connecté dynamique.

Étapes suivantes que vous pouvez faire cette semaine

Si vos paniers ou formulaires cassent, ne commencez pas par changer les TTL. Commencez par prouver quelle couche sert un contenu mis en cache et si elle varie selon le bon état.

  1. Ajoutez des en-têtes debug cache à votre reverse proxy (HIT/MISS/BYPASS et une raison de contournement). Vous vous remercierez plus tard.
  2. Implémentez des exclusions strictes pour cart/checkout/account/login/admin et les endpoints AJAX WooCommerce à chaque couche de cache que vous opérez.
  3. Implémentez un contournement par cookie pour les cookies session/panier WooCommerce et les cookies WordPress d’utilisateurs connectés.
  4. Auditez les cookies et query params utilisés pour devise/langue/geo et décidez : varier, contourner ou repenser.
  5. Déployez Redis comme cache d’objets (si ce n’est pas fait) et confirmez qu’il est réellement actif.
  6. Créez un test synthétique qui récupère /cart/ et /checkout/ et affirme Cache-Control: no-store et l’absence d’en-têtes HIT cache.
  7. Arrêtez les comportements purge-all sauf en urgence. Purgez chirurgicalement et utilisez des stratégies stale pour les pages anonymes si votre proxy le supporte.

La mise en cache est un outil puissant. Traitez-le comme tel. Portez les lunettes de sécurité : en-têtes, règles de contournement et tests qui tournent quand vous ne regardez pas.

← Précédent
Debian 13 : APT cassé (« dépendances non satisfaites ») — réparez-le sans réinstaller
Suivant →
Volumes ZFS à provisionnement fin : le piège du surengagement et comment le surveiller

Laisser un commentaire