PrestaShop soldes : optimiser caches, CDN et Varnish pour trafic élevé

Stratégies concrètes pour tenir les pics de trafic soldes sur PrestaShop : CDN + Varnish comme full‑page cache, ESI pour fragments, purge ciblée et tests de charge.

Serveur avec visualisation d'optimisation de cache et CDN.

Table des matières :

  1. Cache multi-niveaux en période de soldes : ce que vous pouvez (et ne pouvez pas) mettre en cache
  2. Pré-requis concrets (versions, topologie, risques) avant d’ouvrir Varnish et un CDN
  3. Varnish devant PrestaShop : VCL “anti-soldes” (bypass intelligent, TTL, grace, hit-for-pass)
  4. ESI (Edge Side Includes) : fragmenter le “dynamique” pour augmenter le hit ratio sans casser le panier
  5. CDN devant Varnish : cache des assets, cache HTML anonyme, purge, et cache keys qui ne partent pas en vrille
  6. Validation en charge : métriques Varnish/CDN, runbook 503, et tests reproductibles avant J-1

Cache multi-niveaux en période de soldes : ce que vous pouvez (et ne pouvez pas) mettre en cache

Sur une boutique PrestaShop (8.1.x ou 9.0.x) en charge, le « cache » n’est pas un bloc monolithique. Vous avez au minimum : cache navigateur, cache CDN, cache reverse-proxy (Varnish), caches applicatifs (Smarty/Symfony), cache d’objets (Redis/Memcached), et cache d’opcode (OPcache). Pendant les soldes, l’objectif n’est pas « d’activer le cache », mais de déplacer un maximum de requêtes HTTP hors PHP/MySQL sans casser la personnalisation (panier, prix clients, stock, devises, règles panier). Si vous n’avez pas cette cartographie, vous finissez mécaniquement par compenser avec du scaling horizontal coûteux… et vous créez des états incohérents.

Point important : le cœur de PrestaShop ne fournit pas de full page cache robuste. Même en 9.x (Symfony 6.4), une page catégorie reste un assemblage dynamique avec dépendances fortes (prix, taxes, stock, facettes, modules). Résultat : si vous voulez tenir un pic de trafic, le couple CDN + Varnish devient votre “full page cache” pour les visiteurs non authentifiés, et Redis/OPcache sont là pour réduire le coût des hits qui passent quand même au backend. Pour les couches serveur (Redis, OPcache, MySQL), vous pouvez partir d’une base solide via : Cache PrestaShop : Varnish, Redis, Memcached et OPcache côté serveur et Performance PrestaShop : benchmarks et optimisation PHP-FPM, OPCache, MySQL.

Pour éviter les “mauvaises surprises” en période de soldes, clarifiez quoi dépend de quoi, couche par couche. Une règle simple : ce qui dépend d’un identifiant utilisateur (session, panier, groupe client) n’est pas partageable tel quel entre visiteurs.

Couche À cacher en priorité À éviter / attention Indices de bon réglage
Navigateur CSS/JS/images/font en versionné (hash) HTML checkout / compte Cache-Control: public, max-age=..., immutable pour assets
CDN Assets + HTML anonyme court Variation sur cookies / UA / tracking Hit ratio edge stable, purge ciblée
Varnish HTML anonyme + certaines API publiques Panier/compte/paiement, pages dépendantes cookies X-Cache: HIT fréquent sur catégories/produits
Redis/Memcached Résultats de calcul (config, metadata, fragments) Ne masque pas un SQL catastrophique Latence stable, mémoire maîtrisée
OPcache Bytecode PHP Mauvaise invalidation en déploiement opcache.validate_timestamps cohérent avec votre CI/CD

Enfin, le cache HTTP obéit à des règles strictes, et c’est la partie que beaucoup bricolent. L’HTTP caching est normé : la RFC 9111 (HTTP Caching) formalise la notion de fraîcheur, d’expiration, de revalidation et de “staleness” : https://www.rfc-editor.org/rfc/rfc9111. Concrètement, si vos en-têtes Cache-Control, Vary et vos cookies ne sont pas cohérents, ni Varnish ni le CDN ne peuvent “deviner” ce qui est partageable.

Checklist rapide (à faire avant toute optimisation agressive) :

  • Quelles pages sont strictement anonymes (aucune dépendance session, aucun prix B2B, aucun stock “réservé”) ?
  • Quelle est votre stratégie langue/devise (variation par URL, sous-domaine, ou cookie) ?
  • Les pages filtrées (facettes) sont-elles cachées ou bypass (risque de cache key explosive via query strings) ?
  • Avez-vous un header explicite pour diagnostiquer (X-Cache, Age, X-Cacheable, X-Served-By) ?

Pré-requis concrets (versions, topologie, risques) avant d’ouvrir Varnish et un CDN

Les recettes ci-dessous partent d’un setup réaliste en 2026 : PrestaShop 8.1.x ou 9.0.x, PHP 8.2/8.3 (FPM), Varnish 7.4+ (7.5 si dispo), et un frontal Nginx ou HAProxy en terminaison TLS. Le schéma le plus stable en prod : CDN -> (TLS) -> reverse proxy (Nginx/HAProxy) -> Varnish -> Nginx/PHP-FPM -> PrestaShop. Évitez de mettre Varnish en terminaison TLS “à la va-vite” : c’est faisable, mais ça ajoute des contraintes opérationnelles inutiles (certificats, ALPN, HTTP/2). Si vous devez déjà gérer des ACL/rate limit au bord, lisez plutôt : HAProxy reverse proxy : terminaison TLS, rate limiting et supervision Prometheus.

Quelques points pratiques souvent oubliés dans les architectures “soldes” :

  • Propagation des IP client : entre CDN → proxy → Varnish → origin, fixez une règle claire (X-Forwarded-For / Forwarded) sinon vos logs, rate limit et antifraude deviennent inutilisables.
  • HTTP/2 côté edge, HTTP/1.1 côté origin : c’est fréquent et OK, mais attention aux timeouts (keep-alive, pool de connexions) entre Varnish et Nginx.
  • Compression : laissez le CDN gérer Brotli/Gzip côté client ; évitez la double compression incohérente. Varnish peut stocker compressé/décompressé selon config, ce qui impacte la RAM.

Risque principal : mettre en cache du contenu utilisateur (panier, prix négociés, adresse, état de connexion). Les symptômes sont violents (le pire : paniers croisés), et ce n’est pas “un bug Varnish”, c’est votre stratégie cookies/headers qui est mauvaise. Gardez en tête la règle : si une réponse dépend d’un cookie, elle n’est cacheable qu’après normalisation ou séparation de la cache key. Sur PrestaShop, les cookies fréquents incluent PrestaShop-*, parfois des cookies de modules (tracking, AB test), et des cookies de consentement.

Un cas très concret en France/UE : les bannières RGPD/consentement ajoutent souvent un cookie même pour un visiteur anonyme. Si vous faites varier le cache HTML sur ce cookie de consentement, vous risquez un effondrement du hit ratio (plusieurs variantes par visiteur/navigateur). La bonne approche est généralement de neutraliser ces cookies pour les pages publiques (ou de les déplacer en JS côté client), tout en restant conforme à votre implémentation CMP.

Deux garde-fous à poser avant tout : (1) en-têtes explicites côté origin, (2) observabilité de la décision cache (hit/miss/bypass) dans les headers. Exemple côté Nginx (entre Varnish et PHP) pour éviter des caches ambiguës :

# Exemple simplifié : à adapter à votre stack
location ~* \.(css|js|png|jpg|jpeg|webp|svg|woff2)$ {
  expires 30d;
  add_header Cache-Control "public, max-age=2592000, immutable";
}

# HTML : tant que Varnish/CDN ne sont pas explicitement réglés
location / {
  add_header Cache-Control "private, no-store";
}

Une fois Varnish opérationnel, vous remplacez ce no-store par une politique maîtrisée (par exemple Cache-Control: public, s-maxage=... pour l’anonyme, et private, no-store dès qu’on détecte une session). Pour le volet cache objet, si vous n’avez pas Redis proprement sécurisé et monitoré, ne faites pas semblant : Redis PrestaShop : configurer le cache sur VPS ou serveur dédié et Redis sur Linux : installation, configuration et sécurisation production.

Varnish devant PrestaShop : VCL “anti-soldes” (bypass intelligent, TTL, grace, hit-for-pass)

Varnish n’est pas magique : c’est un moteur qui exécute votre VCL. Les règles minimales sont : bypass des routes sensibles, normalisation des cookies, TTL raisonnables, et grace pour absorber les spikes. En soldes, l’accélération vient surtout de la capacité à servir des pages catégorie/produit depuis RAM au lieu de faire 30–200 requêtes SQL et de déclencher des hooks modules coûteux. (Docs Varnish : https://varnish-cache.org/)

Un VCL de prod pour PrestaShop doit a minima exclure : /cart, /order, /checkout, /my-account, /authentication, /module/* (à trier au cas par cas), et évidemment /admin*. Il doit aussi neutraliser la plupart des cookies non utiles à la variation de contenu (consentement, analytics, etc.), sinon vous explosez le taux de miss. Exemple (Varnish 7.x) volontairement conservateur :

vcl 4.1;

import std;

sub vcl_recv {
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # Nettoyage des paramètres de tracking qui polluent la cache key
  if (req.url ~ "(\?|&)(utm_[^=]+|gclid|fbclid)=") {
    set req.url = regsuball(req.url, "(utm_[^=]+|gclid|fbclid)=[^&]+&?", "");
    set req.url = regsub(req.url, "[\?&]$", "");
  }

  # Bypass zones sensibles
  if (req.url ~ "^/(cart|order|checkout|my-account|authentication)" ||
      req.url ~ "^/admin" ) {
    return (pass);
  }

  # Tri des query strings (stabilise la cache key pour facettes/tri)
  set req.url = std.querysort(req.url);

  # Ne pas cacher si on détecte une session/auth (à adapter)
  if (req.http.Cookie ~ "PrestaShop-" || req.http.Authorization) {
    return (pass);
  }

  # Normalisation basique : pas de cookies = meilleure cacheabilité
  unset req.http.Cookie;

  return (hash);
}

sub vcl_backend_response {
  # Respectez les réponses explicitement non-cacheables si l'origin les pose
  if (beresp.http.Cache-Control ~ "no-store|private") {
    set beresp.uncacheable = true;

    # Hit-for-pass : évite que Varnish refasse le même MISS en boucle
    # (utile quand une page est non-cacheable mais très demandée)
    set beresp.ttl = 30s;
    return (deliver);
  }

  # HTML : cache partagé (CDN/Varnish) pour visiteurs anonymes
  if (beresp.http.Content-Type ~ "text/html") {
    set beresp.ttl = 5m;
    set beresp.grace = 30s;
    set beresp.keep = 2m;

    # Debug
    set beresp.http.X-Cacheable = "YES";
  }

  # Static (si jamais Varnish voit passer des assets)
  if (beresp.http.Content-Type ~ "(text/css|application/javascript|image/)") {
    set beresp.ttl = 1h;
  }
}

sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
  } else {
    set resp.http.X-Cache = "MISS";
  }

  # Age aide à comprendre si l'objet vient du cache et depuis combien de temps
  # (utile en debug navigateur)
}

Deux remarques non négociables. Premièrement, ce VCL bypass dès qu’il voit un cookie PrestaShop-* : c’est sûr, mais ça limite le cache HTML aux vrais anonymes. Si votre boutique pose un cookie PrestaShop même pour les anonymes (ça arrive), vous devez nettoyer/whitelister plutôt que bypass tout. Une approche fréquente consiste à :

  • garder un cookie “technique” non-identifiant si besoin,
  • supprimer les cookies analytics/consentement côté Varnish,
  • ne bypass que si un cookie de session/panier est réellement présent.

Deuxièmement, utilisez grace pour éviter que votre cache ne devienne inutile au moindre ralentissement backend : Varnish peut servir un objet légèrement périmé pendant qu’il revalide en arrière-plan (logique proche de stale-while-revalidate). En période de soldes, ça évite le scénario “thundering herd” : un objet expire, 500 requêtes arrivent en même temps, et tout le monde retombe sur PHP-FPM.

Pour une implémentation conteneurisée et pour valider X-Cache, basez-vous sur : PrestaShop Docker : configurer Varnish VCL et valider le cache X-Cache.

ESI (Edge Side Includes) : fragmenter le “dynamique” pour augmenter le hit ratio sans casser le panier

Le modèle qui tient en pic, c’est souvent : HTML global en cache, plus quelques fragments dynamiques non cacheés (mini-panier, header “Bonjour X”, stock temps réel, recommandations). C’est exactement le rôle d’ESI : vous cachez une page en tant que conteneur, et Varnish assemble des sous-requêtes (fragments) au moment du delivery. Ça coûte un peu plus qu’un HIT pur, mais c’est sans commune mesure avec une génération complète côté PHP.

Mini-scénario “soldes” typique : une page catégorie est appelée massivement depuis une campagne e-mail/SMS à 08:00. Sans ESI, vous bypass la page entière à cause du mini-panier en header → vous perdez 90% du potentiel cache. Avec ESI :

  • la page catégorie “conteneur” est servie en HIT (TTL court),
  • le fragment mini-panier est en private, no-store et passe au backend uniquement pour les visiteurs qui ont un panier,
  • un fragment “meilleures ventes soldes” peut être public avec un TTL de 60–300 s.

Le piège, côté PrestaShop, c’est que beaucoup de thèmes/modules injectent du dynamique partout (widgets, blocks, hooks). Si vous n’isolez pas ces zones, vous êtes condamné à bypass la page entière. Pour rendre ESI opérationnel, vous avez besoin : (1) d’une route fragment stable (souvent un contrôleur module), (2) d’un template qui injecte un tag ESI (<esi:include>), (3) d’une politique de cache fragment distincte (souvent Cache-Control: private, no-store sur le fragment panier ; public sur un fragment “best sellers” agrégé). Pour les hooks et leur identification (utile quand vous cherchez “qui rend ce bloc dynamique ?”), voyez : Hooks PrestaShop : rechercher et identifier les hooks dynamiques.

Points de vigilance ESI (qui font la différence en prod) :

  • Limiter N : 2 à 5 fragments maximum sur les pages à très fort trafic. Au-delà, vous remplacez une requête lourde par 10 requêtes moyennes.
  • Rendre les fragments idempotents : pas de logique “ajoute au panier” ou “écrit en session” dans un fragment, sinon vous fabriquez des effets de bord.
  • Stabiliser les URLs de fragments : évitez les query strings “aléatoires” (?ts=...) qui annihilent le cache et saturent le backend.
  • Politique d’erreur : si un fragment tombe, la page doit rester utilisable (dégradation acceptable plutôt que 500 global).

Une implémentation Varnish ESI (avec capacités de substitution) et des exemples de VCL dédiés sont détaillés ici : Varnish Cache : configuration ESI pour optimiser le cache par fragments. Si votre thème ne permet pas d’injecter des tags ESI proprement, c’est un signal : vous avez un problème d’architecture front, pas un problème de reverse proxy.

CDN devant Varnish : cache des assets, cache HTML anonyme, purge, et cache keys qui ne partent pas en vrille

Le CDN n’est pas juste là pour “servir des images”. En soldes, il sert à : (1) absorber des pics géographiques, (2) réduire la latence (TTFB perçu) via edge cache, (3) protéger l’origin (WAF, rate limit), (4) stabiliser la dispo quand l’origin est en tension. Pour les fondamentaux des directives HTTP, MDN est factuel et clair : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control. Si votre CDN ignore vos headers (ou si vous lui forcez un “cache everything” sans discrimination), vous fabriquez du contenu obsolète et vous perdez la maîtrise.

Stratégie pragmatique (qui marche) : assets statiques en cache long (immutable, versionnement par hash), HTML anonyme en cache court (1–10 minutes) avec s-maxage + purge/tag, et bypass total du checkout. Le gain est immédiat : moins de connexions concurrentes, moins de CPU PHP-FPM, et un TTFB stable. Sur cet axe, l’objectif n’est pas une médiane “jolie”, c’est d’éviter les p95/p99 catastrophiques.

À garder en tête côté UX/SEO : les recommandations Core Web Vitals donnent des seuils concrets (par exemple LCP “bon” autour de 2,5 s). Référence : https://web.dev/lcp/. Un CDN bien réglé vous aide surtout sur la distribution et les bursts, Varnish sur la réduction du coût origin.

La partie la plus sous-estimée : la cache key. Si vous laissez le CDN varier sur tout (query string, cookies, user-agent), votre hit ratio tombe à zéro. Décidez explicitement quelles dimensions font réellement varier le HTML :

  • À inclure (souvent) : Host, Accept-Encoding, langue/devise si elles changent le rendu.
  • À exclure/normaliser : utm_*, gclid, fbclid, cookies de tracking/consentement, user-agent (sauf cas extrêmes), ordre des query params.
  • À traiter au cas par cas : Accept-Language (préférez une langue dans l’URL), facettes/tri (risque de cardinalité énorme).

Ensuite, l’invalidation : évitez le “purge all” pendant les soldes. Travaillez par tags (Surrogate-Key) quand vous pouvez, sinon par patterns (moins idéal). Typiquement :

  • mise à jour d’un prix produit : purger product:<id> + pages qui l’incluent (catégories, landing “soldes”),
  • mise à jour stock : TTL très court ou purge à l’événement, selon votre fréquence de mouvements,
  • règles panier / codes promo : prudence, car l’effet peut être global sur des pages produit/catégorie (souvent un bon argument pour garder un TTL bas en période de promos).

Pour aller plus loin sur la coordination CDN/Varnish/CCC : PrestaShop soldes : optimiser le cache multi-niveaux et CCC et, pour l’obsession TTFB, TTFB PrestaShop : réduire le Time To First Byte sous 200 ms.

Validation en charge : métriques Varnish/CDN, runbook 503, et tests reproductibles avant J-1

Sans métriques, vous “optimisez” au doigt mouillé. Vous devez au minimum exposer : hit ratio Varnish (HIT/MISS/PASS), backend_fail, taux de réponses 5xx, p95/p99 de ttfb, saturation PHP-FPM (busy/idle), et latence MySQL. Côté Varnish : varnishstat et varnishlog font le job ; en prod, vous brancherez un exporter Prometheus et vous poserez des alertes sur : baisse brutale de hit ratio, hausse des sess_dropped, hausse des backend_conn et backend_fail. Pour le monitoring applicatif/erreurs, vous avez déjà des briques : PrestaShop monitoring d’erreurs : logs PHP, MySQL, JavaScript et alertes e-mail et Surveillance PrestaShop : tableau de bord, seuils et réduction des fausses alertes.

Seuils “opérationnels” (à adapter) qui aident réellement pendant les soldes :

  • Hit ratio Varnish sur pages catalogue : viser > 70% (en dessous, vous avez souvent un problème de cookies/cache key).
  • backend_fail : toute hausse soutenue est un signal d’origin en souffrance (timeouts, saturation).
  • PHP-FPM : si vos workers sont quasi toujours busy, Varnish/CDN ne compensent plus (effet cascade).
  • MySQL : surveillez le nombre de connexions, les locks, et surtout les requêtes lentes corrélées à un endpoint.

Ensuite, un runbook “soldes” doit inclure des actions simples et testées : comment bypass Varnish en urgence (header/ACL), comment réduire le TTL, comment désactiver un module qui génère des requêtes N+1, comment repasser une page critique en statique (bannière, landing). Les 503 sont souvent un effet de cascade : saturation PHP-FPM → timeouts → Varnish passe tout en pass → le backend s’écroule. Pour la partie diagnostic 503, gardez ce lien sous la main : Erreur HTTP 503 : diagnostic serveur, logs et ressources. Pour profiler SQL côté PrestaShop avant d’accuser “Varnish”, utilisez : PrestaShop debug profiling : activer et analyser performances SQL.

Enfin, faites des tests reproductibles avant la prod : k6/Locust/Gatling, avec scénarios réalistes (landing → catégorie → fiche produit → ajout panier → checkout). Le KPI qui compte pendant les soldes n’est pas seulement la moyenne : c’est la tenue du p95/p99 et l’absence de timeouts.

Exemple de check-list de test (simple mais efficace) :

  • “Warm cache” : un run qui chauffe CDN/Varnish (sinon vous testez surtout le MISS).
  • “Cold-ish cache” : purge ciblée (catégorie/produit) puis reprise du trafic (vous testez l’invalidation).
  • Mélange anonymes / connectés : au moins 80/20 si c’est votre réalité.
  • Variation langue/devise (si applicable) : vérifiez que le hit ratio ne s’effondre pas.
  • Dégradation contrôlée : simulez un backend plus lent (ou limitez PHP-FPM) pour observer l’efficacité de grace.

Si vous n’avez pas de procédure de test + rollback, vous vous tirez une balle dans le pied : PrestaShop performance : monitoring, tests de charge et runbooks soldes et, pour la discipline rollback en général, Migration PrestaShop 9 : sécurité, tests et plan de rollback. L’objectif final est banal mais strict : un cache “prévisible”, invalidable, observable, et qui ne fuit jamais des données utilisateurs — sinon vous n’êtes pas en train d’optimiser, vous êtes en train de gambler votre prod.


À lire aussi