Audit performance PrestaShop : méthode en 6 étapes reproductibles

Méthode pratique en 6 étapes pour diagnostiquer et améliorer les performances PrestaShop : CWV, TTFB, MySQL, modules et cache, avec plan de remédiation.

Processus d'audit illustré avec du code et étapes numérotées.

Table des matières :

  1. Étape 1 — Verrouiller le périmètre, les versions et une baseline “réexécutable”
  2. Étape 2 — Mesurer le front “comme un navigateur” (CWV, waterfall, budgets)
  3. Étape 3 — Isoler le TTFB et la chaîne serveur (reverse-proxy → PHP-FPM → PrestaShop)
  4. Étape 4 — Prouver (ou disculper) MySQL : slow queries, EXPLAIN, indexation ciblée
  5. Étape 5 — Traquer le coût réel des modules, overrides et du thème (profiling + hooks)
  6. Étape 6 — Convertir les constats en plan de remédiation vérifiable (et prévenir les régressions)

Un audit performance PrestaShop n’a d’intérêt que s’il est reproductible : mêmes pages testées, mêmes jeux de données, mêmes métriques, mêmes scripts, et surtout la capacité de rejouer l’audit après chaque release (core, thème, modules, infra). La majorité des “audits” échouent parce qu’ils mélangent de l’observation qualitative (waterfall DevTools) et des optimisations au doigt mouillé, sans baseline ni validation.

Le cadre ci-dessous vise PrestaShop 8.1/8.2 et PrestaShop 9.x (architecture Symfony 6.4 côté BO + partie FO), avec PHP 8.2 ou 8.3 (et PHP-FPM), MySQL 8.0/MariaDB 10.6+ et un reverse-proxy possible (Varnish/HAProxy). Adaptez si vous êtes encore en 1.7 : les symptômes sont proches, mais l’outillage moderne (profiler Symfony, instrumentation) est plus compliqué.

« Core Web Vitals are the subset of Web Vitals that apply to all web pages, should be measured by all site owners… » — web.dev (Google), documentation Core Web Vitals : https://web.dev/vitals/

Étape 1 — Verrouiller le périmètre, les versions et une baseline “réexécutable”

Commencez par figer ce que vous auditez : liste des templates/pages (home, catégorie, fiche produit, recherche, panier, checkout), devices (mobile/desktop), géos, langues, et scénarios (utilisateur anonyme vs connecté, avec/ sans combinaison, avec/ sans promo). En e-commerce, les perfs perçues explosent dès qu’on change de contexte (ex : recherche ElasticSearch activée, ou panier avec règles complexes). Si vous ne verrouillez pas le périmètre, vous comparez des pommes et des oranges.

Pour éviter les angles morts, formalisez votre périmètre comme une matrice (simple, mais explicite) :

Type de page URL(s) de référence Variantes métier Cache attendu
Home / anonymes (bannières, blocs) cacheable
Catégorie /12-categorie tri, pagination, facettes cacheable (souvent)
Produit /produit/x déclinaisons, stock, prix barré cacheable si pas de personnalisation
Recherche /recherche?query=... moteurs, auto-complétion rarement cacheable
Panier /panier règles panier, frais port non-cacheable
Checkout /commande transporteurs, paiement non-cacheable

Ajoutez un volet géolocalisation réaliste : si votre clientèle est majoritairement en France métropolitaine, testez au minimum depuis une localisation “proche” (ex. Paris) et une localisation plus éloignée (ex. sud de la France ou pays limitrophe) pour valider que la perf ne dépend pas uniquement de la proximité du datacenter ou d’un POP CDN. L’objectif n’est pas de “faire du multi-geo pour faire joli”, mais de détecter des écarts (latence, routage, TLS, cache CDN) qui impactent directement LCP et TTFB.

Ensuite, figez le triplet version/config : PrestaShop (ex. 8.2.1 ou 9.1.x), PHP (8.2.x), MySQL (8.0.x), thème (commit), modules (liste + versions), et paramètres de cache. Le cœur PrestaShop est suffisamment “hook-driven” pour qu’un seul module modifiant displayHeader ou actionFrontControllerSetMedia fasse dériver LCP/INP sans que ça se voie au premier coup d’œil. Documentez ces infos dans un AUDIT.md versionné.

Un AUDIT.md utile ressemble plus à une “fiche d’identité” rejouable qu’à un rapport narratif. Par exemple (à adapter) :

  • Environnement : staging/prod, URL, date/heure, datacenter/region, CDN oui/non
  • Versions : PrestaShop, PHP, MySQL/MariaDB, Nginx/Apache, Varnish, Redis
  • Thème : nom + commit + mode build (minification, bundling)
  • Modules : export complet + modules “impactants” (assets globaux, tracking, search, avis, chat)
  • Cache : règles Cache-Control, pages exclues, stratégie d’invalidation, TTL, ESI oui/non
  • Réseau de test (lab) : device, throttling, viewport, nombre de runs, médiane retenue
  • Jeu de données : dump anonymisé/versionné ou seed, nombre de produits/combinaisons, règles panier, langues/devises
  • Commande de replay : scripts curl, k6, Lighthouse CI + paramètres

Enfin, produisez une baseline mesurable et rejouable : (1) un jeu de données stable (dump anonymisé, ou seed minimal), (2) un script de warmup (pré-chauffage cache applicatif + OPcache + Varnish si présent), (3) des commandes de test (curl/k6/Lighthouse CI) committées. L’objectif est de pouvoir dire : “sur staging, commit X, avec warmup Y, la page produit p95 = …”.

Point souvent négligé : documentez aussi les invariants de navigation (cookies, devise, pays de livraison, langue). Entre une page produit “France / EUR / livraison standard” et “Suisse / CHF / taxes / transporteur différent”, vos calculs peuvent diverger, donc votre TTFB aussi.

Sur la partie méthode et scripts de test de charge, vous pouvez croiser avec l’approche déjà détaillée dans PrestaShop performance : monitoring, tests de charge et runbooks soldes et la logique de build reproductible côté CI dans Intégration continue PrestaShop : BuildKit, GitHub Actions et build reproductible.

Étape 2 — Mesurer le front “comme un navigateur” (CWV, waterfall, budgets)

Ne confondez pas performance serveur et performance utilisateur. Pour le front, vos métriques structurantes sont aujourd’hui LCP, INP et CLS (Core Web Vitals), plus des métriques “diagnostic” (TTI n’est plus centrale, mais le long task time et le Total Blocking Time restent utiles en lab). Faites au minimum un triptyque : Lighthouse (lab), PageSpeed Insights (lab + CrUX si dispo), et WebPageTest (waterfall + filmstrip). Répétez sur 3 à 5 runs, conservez le médian, et surveillez les percentiles (p75/p95), pas seulement la moyenne.

Pour éviter de “corriger un score” au lieu d’améliorer une expérience, reliez chaque métrique à une cause probable :

  • LCP : image héro trop lourde, CSS bloquant, TTFB élevé, rendu retardé par JS, polices, sliders.
  • INP : JS tiers, handlers lourds (filtre, quickview), listeners multiples, long tasks.
  • CLS : images sans dimensions, blocs injectés (bannières, avis, chat), polices swap mal géré.

Pour rendre la mesure reproductible, passez sur Lighthouse CI (ou un runner Headless stable) avec une config figée (throttling, device, viewport). Définissez des performance budgets par type de page : ex. JS total < 250–350 kB compressé mobile, CSS < 80–120 kB, images héro optimisées (AVIF/WebP), et nombre de requêtes limité. Une boutique PrestaShop “standard” dépasse très vite ces budgets à cause de l’empilement thème + modules + trackers.

Un budget qui aide concrètement à arbitrer (sans tomber dans la rigidité) :

  • Home : beaucoup de marketing, mais pas une excuse pour 1 Mo de JS.
  • Catégorie : priorité à l’interaction (filtres/tri), donc focus sur INP et long tasks.
  • Produit : priorité au LCP (image), puis à l’INP (combinaisons, ajout panier).
  • Checkout : priorité à la stabilité et à l’absence de scripts non essentiels (tags, chat, AB tests).

Si vous voulez une vérité terrain, instrumentez du RUM (Real User Monitoring) sur un échantillon : la librairie web-vitals permet de remonter LCP/INP/CLS côté navigateur. Exemple minimal (à pousser dans votre pipeline analytics interne, ou même vers un endpoint maison pour audit) :

import {onLCP, onINP, onCLS} from 'web-vitals';

function send(metric) {
  navigator.sendBeacon('/_perf', JSON.stringify(metric));
}

onLCP(send);
onINP(send);
onCLS(send);

Deux garde-fous côté e-commerce (souvent oubliés) :

  • Échantillonnage + RGPD : même si les métriques CWV ne sont pas des “données personnelles” en soi, votre endpoint /_perf peut capter IP/UA côté serveur. Anonymisez, minimisez, et documentez la finalité (audit perf).
  • Segmentation : segmentez au moins par type de page et device. Un p75 global “correct” peut masquer un checkout catastrophique sur mobile.

C’est la seule façon d’éviter l’angle mort “mon Lighthouse est vert, mais mes vrais mobiles 4G souffrent”. Pour l’analyse fine des waterfalls et du rendu, la méthodologie détaillée dans DevTools : méthodes professionnelles pour mesurer et optimiser les performances web et les actions correctives CWV dans Core Web Vitals : actions concrètes pour améliorer LCP, INP et CLS servent de référentiel.

Étape 3 — Isoler le TTFB et la chaîne serveur (reverse-proxy → PHP-FPM → PrestaShop)

Le TTFB (Time To First Byte) reste un indicateur brutal mais utile : si la première réponse met 800 ms, vous ne “rattraperez” pas ça avec du lazy-loading. Mesurez-le proprement (sans cache navigateur) :

curl -s -o /dev/null -w 'ttfb=%{time_starttransfer} total=%{time_total}\n' \
  -H 'Cache-Control: no-cache' \
  https://votre-boutique.tld/produit/x

Astuce de reproductibilité : faites vos curl depuis la même machine (runner CI, bastion) et notez la résolution DNS (un CDN peut vous envoyer vers un POP différent selon l’origine). Si vous suspectez un effet “géographique”, comparez à méthode identique : même commande, même URL, mais point de sortie différent (ex. VPN/runner dans une autre région).

La question n’est pas “TTFB élevé” mais “où ça bloque” : DNS/TLS, reverse-proxy (HAProxy/Varnish/Nginx), application PHP, ou DB. Sur Nginx, loguez explicitement les temps upstream (et conservez-les au format structuré si vous faites du SIEM/ELK) :

log_format timed '$remote_addr $request $status '
  'rt=$request_time urt=$upstream_response_time uct=$upstream_connect_time';
access_log /var/log/nginx/access.log timed;

Interprétez ces temps comme une mini-chaîne :

  • request_time haut + upstream_response_time bas → souvent réseau client, envoi, ou buffering.
  • upstream_response_time haut → la lenteur est côté application (PHP) ou backend (DB/HTTP sortants).
  • upstream_connect_time haut → saturation ou latence entre reverse-proxy et PHP-FPM (ou mauvais keepalive).

Côté PHP-FPM, activez pm.status_path, observez saturation (listen queue, max children reached), et validez OPcache (hit rate, revalidate_freq) : une mauvaise config OPcache ou un pm.max_children sous-dimensionné fait exploser p95/p99 pendant les pics. Les réglages concrets (PHP-FPM, OPcache, MySQL) sont déjà traités dans Performance PrestaShop : benchmarks et optimisation PHP-FPM, OPCache, MySQL ; gardez cet article comme base de tuning avant d’accuser le code.

Enfin, vérifiez la stratégie de cache serveur : cache HTTP (Varnish), cache applicatif (Redis/Memcached), cache Symfony/PrestaShop, et invalidation. PrestaShop a un historique de comportements “sur-invalidation” (tout vider pour un petit changement), souvent aggravé par des modules. Si votre TTFB s’effondre en cache chaud mais s’écroule en cache froid, vous avez un problème d’invalidation ou de fragmentation (ESI).

Mini-scenario très fréquent : une home “cacheable” appelle un endpoint non cacheable (bloc panier, recommandations, chat), ce qui force des variations (Vary: Cookie) et réduit drastiquement le taux de hit. Résultat : en apparence “Varnish est là”, mais le trafic est servi majoritairement en pass.

Pour la couche cache, appuyez-vous sur Cache PrestaShop : Varnish, Redis, Memcached et OPcache côté serveur et, si vous utilisez Varnish, sur Varnish Cache : configuration ESI pour optimiser le cache par fragments. Pour un objectif TTFB agressif (< 200 ms sur pages cacheables), la démarche est détaillée dans TTFB PrestaShop : réduire le Time To First Byte sous 200 ms.

« Latency is the new bandwidth. » — Ilya Grigorik, High Performance Browser Networking (O’Reilly). Cette phrase est devenue un résumé utile : quand la latence est mauvaise, vous payez partout (handshakes, requêtes, DB, IO), même si vous avez “de la bande passante”.

Étape 4 — Prouver (ou disculper) MySQL : slow queries, EXPLAIN, indexation ciblée

La base de données est le premier suspect… et pourtant, sur PrestaShop, elle est souvent complice plutôt que coupable : un module ajoute une jointure inutile, une recherche exécute une requête non couverte, ou une page déclenche 200 requêtes à cause de hooks. Il faut donc d’abord instrumenter : activez le slow query log avec un seuil réaliste (ex. 100–200 ms en staging, plus haut en prod si charge) et log_queries_not_using_indexes uniquement pour audit (c’est bruyant).

Pour que l’analyse serve vraiment, capturez aussi le contexte :

  • Fréquence : 1 requête à 2 s peut être moins grave qu’une requête à 60 ms exécutée 2 000 fois.
  • Variance : une requête “moyenne” peut cacher des cas pathologiques (p95/p99).
  • Origine : page/contrôleur/hook à l’origine (sinon vous corrigez au hasard).

Analysez ensuite avec pt-query-digest (Percona Toolkit) ou un agrégateur équivalent : vous cherchez des requêtes fréquentes, lentes, ou avec grande variance (p95 très haut). Sur PrestaShop, des points chauds typiques : calculs de prix (specific prices), stock, navigation à facettes, listings catégorie (ORDER BY), et recherche. Le résultat attendu d’un audit n’est pas “il y a des requêtes lentes”, mais une liste actionnable : requête signature, origine PHP, cardinalité, index manquant, plan EXPLAIN, gain estimé.

Une manière “audit-proof” de présenter les constats (exemple de format) :

  • Requête : SELECT ... FROM ps_specific_price ...
  • Contexte : fiche produit (produits avec déclinaisons + promos)
  • Symptôme : filesort + lecture de X lignes, exécution répétée
  • Hypothèse : index composite non adapté à la combinaison WHERE + ORDER BY
  • Action : index ciblé + réduction appels (cache applicatif / regroupement)
  • Validation : avant/après sur p95 TTFB produit + coût CPU DB + EXPLAIN

C’est ici que EXPLAIN (voire EXPLAIN ANALYZE selon moteur) devient non négociable : vous devez voir si MySQL fait un full scan, un filesort, ou utilise le bon index composite (ordre des colonnes cohérent avec WHERE puis ORDER BY). La méthode d’optimisation d’index (et les pièges classiques) est explicitée dans Index MySQL : optimiser WHERE, JOIN et ORDER BY avec EXPLAIN.

Deux précautions importantes dans PrestaShop :

  • N+1 “silencieux” : côté PHP, un listing peut appeler une fonction qui interroge la DB par produit (prix, stock, labels). Le slow log peut ne rien montrer de “très lent”, mais la somme tue la page.
  • Données parasites : tables de logs, paniers abandonnés, sessions, ou tables de modules laissées sans purge. Un COUNT(*) ou un JOIN devient lent parce que la table a grossi, pas parce que MySQL “régressait”.

Et pour éviter de diagnostiquer sur une base polluée (tables de logs gigantesques, paniers abandonnés, sessions), une routine de maintenance est documentée dans Base de données PrestaShop : routine de maintenance et nettoyage automatisé.

Étape 5 — Traquer le coût réel des modules, overrides et du thème (profiling + hooks)

Le cœur PrestaShop n’est pas “lent” par nature ; il est extensible, donc imprévisible. L’audit doit isoler l’impact des modules, overrides et surcharges de classes. Concrètement : exportez la liste des modules activés, identifiez ceux qui injectent du JS/CSS globalement, et vérifiez les hooks critiques (displayHeader, displayFooter, actionDispatcher, actionFrontControllerSetMedia, etc.). Un module qui ajoute 3 requêtes et 2 appels HTTP sortants sur chaque page produit peut détruire INP et TTFB sans déclencher d’erreur.

Pour rendre ce travail “scientifique” (et acceptable côté métier), procédez par itérations contrôlées :

  1. Regroupez les modules par familles : tracking, search, merchandising, avis, chat/support, paiement, transport.
  2. Désactivez par lots en staging (ou via feature flags) pour mesurer un delta clair (TTFB/LCP/INP + requêtes DB).
  3. Réactivez un par un uniquement dans le lot “coupable” jusqu’à identifier le(s) contributeur(s).

Côté thème, cherchez les coûts structurels :

  • CSS/JS chargés partout (même checkout), bundles non séparés par page.
  • Composants “riches” (carrousels, quickview, infinite scroll) qui ajoutent du JS et des long tasks.
  • Fonts : trop de variantes, preloading mal calibré, font-display non adapté.
  • Images : hero non optimisée, dimensions manquantes (CLS), absence de responsive images.

Côté profiling, évitez de vous limiter au chrono global. Sur PrestaShop 9, vous pouvez exploiter plus facilement l’écosystème Symfony (toolbar/profiler en environnement de debug contrôlé). Sinon, mettez en place un profiler PHP (Xdebug en staging, Tideways/Blackfire selon contraintes) pour obtenir des flamegraphs : temps CPU, allocations, appels réseau.

Ce que vous cherchez typiquement dans un flamegraph PrestaShop :

  • Surcharges/overrides qui recalculent des données déjà disponibles (prix, règles panier).
  • Appels externes (API avis, recommandations, tracking server-side) synchrones dans le rendu.
  • Serialisation/templating coûteux : trop d’assignations Smarty/Twig, boucles profondes, fragments non cachés.
  • Gestion d’assets : génération/combinaison en runtime au lieu d’être faite au build.

C’est la seule façon de prouver qu’un handler de hook ou une surcharge de Product::getPriceStatic() est en train de faire du travail inutile. Pour la qualité et l’industrialisation du code (et donc la prévention de régressions), PHPStan et Rector : industrialiser la qualité du code PHP apporte des garde-fous utiles (typage, détection de code mort, patterns risqués).

Enfin, il faut traiter le sujet qui fâche : la désinstallation “classique” des modules laisse souvent des résidus (tables, overrides, hooks) et complique l’audit (vous croyez avoir désactivé un module, mais il reste du code exécuté). La démarche propre (et ses impacts perf/sécurité) est détaillée dans Modules PrestaShop : désinstallation propre, performances et sécurité en production.

Pour les développeurs qui refactorent ou créent des modules, Module PrestaShop 9 : structure, services et bonnes pratiques Symfony et Symfony PrestaShop : développer des modules robustes avec Doctrine aident à éviter les patterns qui finissent en N+1 ou en surcoûts d’initialisation.

« Make it work, make it right, make it fast. » — Kent Beck. En PrestaShop, “make it fast” doit être contraint par des mesures (p95/p99), sinon vous optimisez des illusions.

Étape 6 — Convertir les constats en plan de remédiation vérifiable (et prévenir les régressions)

Un audit performance PrestaShop utile se termine par un plan chiffré : actions, ordre, risques, validation. Priorisez selon (impact utilisateur) × (fréquence page) × (effort) × (risque). Exemple : réduire 400 ms de TTFB sur les pages catégorie peut rapporter plus qu’optimiser 200 ms sur une page CMS peu vue. Fixez des objectifs explicites : p75 LCP < 2,5 s sur mobile, INP < 200 ms, CLS < 0,1, et côté serveur p95 TTFB < 200–300 ms sur pages cacheables (plus haut si non-cacheable, mais documenté).

Pour rendre le plan exploitable, formalisez-le sous forme de backlog “audit → action → preuve”, par exemple :

Action Hypothèse Risque Mesure avant/après Critère d’acceptation
Optimiser image LCP produit LCP dominé par hero faible LCP lab + RUM p75 -X ms LCP, poids image réduit
Réduire JS tiers sur catégorie INP dominé par long tasks moyen INP + TBT lab INP sous seuil + pas de régression métier
Index DB sur listing filesort/scan moyen slow log + p95 TTFB baisse p95 + plan EXPLAIN amélioré
Ajuster PHP-FPM + OPcache saturation en pic moyen p95/p99 + FPM status plus de “max children reached”

La validation doit être outillée : tests de charge (k6/locust), tests Lighthouse CI, et tests fonctionnels (tunnel commande) pour éviter les “optimisations” qui cassent le métier. Un exemple minimal de test k6 reproductible (pensez à gérer cookies et cache) :

import http from 'k6/http';
import { sleep } from 'k6';

export const options = { vus: 20, duration: '2m' };

export default function () {
  http.get('https://votre-boutique.tld/');
  http.get('https://votre-boutique.tld/12-categorie');
  http.get('https://votre-boutique.tld/produit/x');
  sleep(1);
}

Pour rendre ce test “audit-grade”, complétez-le généralement avec :

  • un warmup (cache/CDN/Varnish) séparé du run mesuré ;
  • des scénarios différenciés (anonyme vs connecté, ajout panier, navigation catégorie avec pagination) ;
  • une collecte serveur en parallèle (temps upstream, CPU, DB, taux hit cache), sinon vous ne saurez pas pourquoi ça tient (ou casse).

Pour cadrer la non-régression sur le checkout, gardez une checklist exécutable (et versionnée) comme celle de Contrôle PrestaShop post-modification : checklist tunnel de commande et règles panier. Et si vous industrialisez la livraison, branchez ces tests dans votre CI (cf. l’article CI mentionné à l’étape 1) avec des seuils bloquants.

Dernier point : sans observabilité, vous referez le même audit tous les 3 mois. Ajoutez (1) un tableau de bord p95/p99 (TTFB, temps DB, erreurs), (2) une politique de logs exploitable et non verbeuse, (3) un runbook “pics de charge”. Sur un e-commerce en France, anticipez aussi les périodes prévisibles (soldes, Black Friday, campagnes TV, relais influence) : ce sont des moments où la perf se dégrade “vite” et où l’on a besoin d’un protocole clair (dégradations acceptables, actions, rollback).

Sur la gestion des logs et l’auditabilité des changements (utile aussi pour expliquer une régression de perf liée à un module), la méthode décrite dans Automatisation PrestaShop : sécurité RGPD, moindre privilège et journaux d’audit est un bon complément.

Si vous devez activer des modes debug/profiler, faites-le en staging et gardez un environnement prod propre (cf. Gestion d’erreur PHP : bonnes pratiques et configuration développement/production) : la “perf” ne justifie pas de dégrader la sécurité ou de fuiter des données.


À lire aussi