Table des matières :
- TTFB PrestaShop : ce que vous visez quand vous annoncez « < 200 ms »
- Mesurer le TTFB correctement (lab vs prod) et l’attribuer à une cause
- Infra : quand le TTFB est plombé avant même d’arriver à PHP (DNS, TLS, reverse proxy, WAF)
- PHP-FPM + OPcache : 200 ms sans cache page = rarement, mais 200 ms sur cache applicatif = souvent
- Base de données : la plupart des TTFB “inexplicables” sont des requêtes SQL et des verrous
- Viser < 200 ms : sans full-page cache côté anonyme, vous vous tirez une balle dans le pied
- Plan d’action opérationnel (et critères de validation) pour tenir le TTFB sous 200 ms
TTFB PrestaShop : ce que vous visez quand vous annoncez « < 200 ms »
Le TTFB (Time To First Byte) est un indicateur simple en apparence, mais très souvent mal interprété dans un stack e-commerce. La définition la plus utile côté diagnostic est celle utilisée par Google sur web.dev — TTFB : « TTFB measures the time between the request for a resource and when the first byte of a response begins to arrive. » Autrement dit, on est avant le transfert du HTML, et bien avant les images, JS et CSS. Vous mesurez principalement : latence réseau + négociation TLS + temps de traitement serveur + attente de l’upstream (PHP-FPM) + attente DB.
Sur PrestaShop, le TTFB est rarement « juste un problème PHP ». Le cœur a un bootstrap coûteux (autoload, initialisation du contexte, chargement de la config, hooks, modules, parfois Symfony côté FO selon versions/contrôleurs), et ce coût s’empile avec l’infra (TLS, DNS, proxy, WAF, CDN). Un TTFB sous 200 ms est réaliste sur un cache-hit (page anonyme servie par Varnish/CDN), et beaucoup plus difficile sur un cache-miss (HTML rendu par PHP + SQL), surtout en trafic concurrent.
Le piège le plus courant, c’est de parler de « TTFB serveur » alors que la mesure côté navigateur inclut souvent une partie réseau non négligeable. En pratique, il faut toujours distinguer :
- TTFB “vu client” : ce que Lighthouse/Chrome/les utilisateurs mesurent (inclut le chemin réseau réel).
- Temps “backend pur” : ce que Nginx/Apache mesure (requesttime, upstreamresponse_time) et ce que l’APM attribue au code/DB.
Le seuil de 200 ms doit donc être posé avec des critères clairs : quelle page (home, catégorie, produit), quel profil (anonyme vs connecté), quel pays (latence), quel mode (cache-hit vs miss), quel percentile (p50 vs p95). Sans ça, vous allez “optimiser” au hasard.
Pour cadrer l’objectif (et éviter les débats stériles), vous pouvez formaliser une grille simple :
| Contexte | Cible TTFB réaliste | Commentaire |
|---|---|---|
| Page anonyme cache-hit (Varnish/CDN) | < 200 ms (p95) | Atteignable si cookies/variations maîtrisés |
| Page anonyme cache-miss (PHP + SQL) | < 600–900 ms (p95) | Très dépendant modules + DB + concurrence |
| Page connectée (compte, panier) | < 800–1200 ms (p95) | Peu cacheable, cherchez surtout la stabilité |
| Clients éloignés de l’origine (ex. origine en UE, clients hors UE) | + latence réseau | Raison de plus pour un edge CDN bien réglé |
Pour relier le sujet à l’impact réel côté SEO/UX, recoupez avec vos Core Web Vitals (TTFB n’est pas un CWV, mais il conditionne très souvent le LCP) : voir le guide interne sur les actions Core Web Vitals (LCP, INP, CLS).
Enfin, ajoutez une touche “géolocale” sans surinterpréter : une boutique dont la majorité des clients est en France/Belgique/Suisse n’a pas les mêmes contraintes qu’un shop avec trafic significatif en Amérique du Nord. L’objectif n’est pas de “tricher” avec un chiffre global, mais d’aligner l’hébergement (région), le CDN (PoP) et vos mesures (points de test) sur votre réalité business.
Mesurer le TTFB correctement (lab vs prod) et l’attribuer à une cause
Mesurer le TTFB dans Chrome DevTools est pratique, mais insuffisant si vous ne distinguez pas réseau et traitement serveur. En “lab”, commencez avec curl pour isoler un maximum le navigateur et obtenir un résultat reproductible (en visant une page HTML, pas un asset). Exemple :
curl -sS -o /dev/null \
-w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" \
"https://www.example.com/"
Quelques raffinements utiles pour éviter de mesurer “n’importe quoi” :
- Forcez HTTP/2 si votre front le supporte (
--http2) pour être cohérent avec la majorité des navigateurs modernes. - Pour comparer cache-hit vs cache-miss, envoyez volontairement des headers qui influencent le cache (ex.
Cache-Control: no-cache) uniquement si vous savez ce que fait votre CDN/proxy avec ces directives. - Testez une URL stable (home) et une URL “chargée” (produit/catégorie représentatifs). Évitez une page qui redirige (301/302), sinon vous mélangez deux transactions.
- Si vous avez un CDN, comparez une requête “client normal” et une requête qui force l’origine (via un host d’origine interne, ou
--resolvesi c’est maîtrisé côté sécurité).
Répétez 20 à 50 fois, calculez p50/p95, et faites-le depuis plusieurs régions (ou au minimum depuis la même zone que vos clients). Une seule mesure n’a aucune valeur. Si vous avez un CDN, testez aussi l’URL “origine” (host interne ou en forçant la résolution DNS) pour séparer le cache edge des performances du serveur.
Pour l’attribution, activez des métriques de serveur web. Sur Nginx, loggez request_time et upstream_response_time (temps passé à attendre PHP-FPM). Exemple de format de log (attention à l’impact disque sur gros trafic, à cadrer via sampling) :
log_format timed '$remote_addr - $host "$request" '
'status=$status rt=$request_time urt=$upstream_response_time '
'uaddr=$upstream_addr cache=$upstream_cache_status';
access_log /var/log/nginx/access.log timed;
Lecture rapide (très opérationnelle) :
rtélevé eturtélevé → le problème est plutôt côté PHP/DB (ou upstream).rtélevé maisurtfaible → cherchez plutôt côté front (WAF, proxy, buffering, TLS, disk I/O des logs, etc.).urtvide → réponse servie sans upstream (cache proxy, fichier statique, erreur avant PHP).
Enfin, en prod, ne vous limitez pas au lab. Faites du RUM (Real User Monitoring) ou, à défaut, exposez un header Server-Timing pour corréler côté client (c’est lisible dans DevTools et exploitable par certains outils). Référence utile et stable : MDN — Server-Timing.
Le diagnostic “pro” se fait avec APM/profiling (Blackfire, New Relic, Datadog) et avec des méthodes de mesure web détaillées ; pour un protocole complet, vous pouvez suivre l’article interne sur les méthodes professionnelles DevTools.
Infra : quand le TTFB est plombé avant même d’arriver à PHP (DNS, TLS, reverse proxy, WAF)
Avant d’optimiser PrestaShop, vérifiez les “faux” TTFB élevés dus à l’infra. Sur une boutique derrière un WAF/CDN, le navigateur mesure le TTFB du point de présence (edge) si la ressource est cachée, ou celui de l’origine si elle ne l’est pas. Un edge mal configuré (cache bypass systématique à cause de cookies, ou Cache-Control incohérent) vous fait payer la latence + le compute à chaque requête. Si vous avez HTTP/3/QUIC activé côté CDN, vous pouvez réduire une partie des coûts de handshake, mais ça ne sauvera pas une origine lente.
Sur Nginx/Apache, le TTFB peut exploser avec des réglages “par défaut” : keep-alive court, TLS non optimisé, OCSP stapling absent, compression inutilement coûteuse au mauvais endroit (compression côté PHP au lieu du serveur). Sur TLS, privilégiez TLS 1.3 et la reprise de session, vérifiez que vous n’avez pas de chaîne de certificats mal servie.
Checklist rapide “infra avant PHP” (souvent rentable en < 1h de travail) :
- DNS : résolution rapide côté client, TTL cohérent, pas de chaînes CNAME inutilement longues.
- Redirects : pas de 301/302 superflus (http→https, www→non-www, slash, langue). Chaque redirection peut ajouter un TTFB supplémentaire.
- Keep-Alive : suffisamment long côté front pour limiter les connexions neuves (surtout sur mobile).
- TLS : TLS 1.3 activé, ciphers raisonnables, OCSP stapling si applicable.
- WAF : règles lourdes (inspection poussée) limitées aux endpoints sensibles (login, API), et pas sur tout le catalogue.
- Proxy buffering : attention aux settings de buffering/timeout qui peuvent masquer une saturation et transformer un problème “backend” en lenteur diffuse.
Même un détail comme un resolver DNS lent côté serveur (quand PrestaShop appelle des APIs) peut impacter indirectement le TTFB : l’appel API dans un hook pendant le rendu HTML, c’est du temps dans upstream_response_time. Exemple concret : un module de paiement/livraison qui “vérifie” une disponibilité en temps réel sur la page produit ; si l’API met 300 ms à répondre (ou pire, timeouts), votre TTFB devient mécaniquement instable.
Si vous utilisez un reverse proxy (Varnish, Nginx en front d’Apache, Traefik, etc.), loggez chaque couche. L’objectif est de savoir si vous perdez 80 ms en réseau + 600 ms en upstream (PHP), ou 600 ms dans le proxy à attendre une origine saturée. Et si vous avez renforcé la sécurité (WAF, journalisation, SIEM), surveillez les effets de bord : inspection profonde, règles trop coûteuses, ou rate limiting mal calibré. Pour la partie sécurité “sans tuer la perf”, voir l’article interne : Sécurité PrestaShop — WAF et journalisation.
PHP-FPM + OPcache : 200 ms sans cache page = rarement, mais 200 ms sur cache applicatif = souvent
Contexte versions (important) : les recommandations ci-dessous sont valables pour PrestaShop 8.x et 9.x. En 2026, les shops sérieux tournent généralement sur PHP 8.2/8.3 (et parfois 8.4 selon contraintes modules). Pour les détails de compatibilité côté PrestaShop 9.1 (notamment l’écosystème PHP 8.1–8.5), voir l’article interne : Compatibilité PrestaShop 9.1 & PHP.
Le premier levier côté PHP, c’est OPcache. Sans OPcache correctement dimensionné, vous payez du CPU et du disque à chaque requête (recompilation, stat des fichiers, fragmentation). Réglages typiques (à adapter à la taille de code + modules, et à valider via opcache_get_status() et la page phpinfo() en environnement sécurisé) :
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=100000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.jit=0
Deux points qui font souvent la différence sur PrestaShop :
- Capacité OPcache : si
opcache_get_status()montre un taux d’utilisation proche de 100% ou de la fragmentation, vous allez voir des “micro-latences” et des variations de TTFB. - Déploiements :
validate_timestamps=0est un choix production (déploiement via release immuable + reload FPM). Si vous faites du “FTP en prod”, ce réglage va vous piéger (code non rechargé, incohérences).
Deuxième levier : PHP-FPM. Les defaults pm.max_children sous-dimensionnés provoquent des files d’attente (le symptôme typique est un TTFB aléatoire, p95/p99 qui s’envole). Monitorer est obligatoire : activez pm.status_path et exportez les métriques (queues, max children reached).
Un repère simple (à adapter) : si un worker PHP consomme typiquement 80–150 Mo en charge sur votre shop (modules, templates, pics), dimensionnez pm.max_children en partant de la RAM réellement disponible après DB/OS/cache, puis vérifiez en situation réelle. Un pm.max_children trop haut peut être pire qu’un pm.max_children trop bas (swap = TTFB catastrophique).
request_terminate_timeoutpour éviter qu’un appel externe bloquant (API) immobilise un worker indéfiniment.- logs d’erreurs FPM propres (les warnings/notice massifs peuvent dégrader CPU + I/O).
- analyse des endpoints lents (souvent
category,product, mais aussimodule/*).
Enfin, côté applicatif, PrestaShop n’est pas un micro-framework minimaliste. Le coût de bootstrap (Context, hooks, modules) est réel et varie énormément selon le catalogue et les modules installés. En pratique, le meilleur compromis pour viser un TTFB bas sans full-page cache est d’augmenter le hit ratio des caches internes (Smarty, cache des requêtes, cache d’objets, etc.) et de supprimer les modules qui ajoutent des requêtes ou des appels réseau dans le FO.
Mini-scenario très fréquent : une boutique a “seulement” 200 produits, mais 35 modules actifs côté front (tracking, sliders, FAQ, avis, cross-sell). Chaque module ajoute 1–3 requêtes SQL et parfois une requête HTTP. Résultat : le TTFB s’envole, même sur un serveur correct. Dans ce cas, la meilleure optimisation est organisationnelle : inventaire des hooks, suppression/fusion des fonctionnalités, et refonte des modules les plus coûteux.
Si vous industrialisez la qualité code/perf de vos modules, outillez-vous (analyse statique, refactor, CI) : PHPStan & Rector — industrialiser la qualité du code.
Base de données : la plupart des TTFB “inexplicables” sont des requêtes SQL et des verrous
Sur PrestaShop, un TTFB élevé en cache-miss est souvent un mix de : requêtes non indexées, JOIN coûteux sur tables volumineuses, tri sur colonnes non indexées, et surtout contention InnoDB (verrous) en pic de trafic (ex. écritures panier, sessions, statistiques, logs). Le diagnostic commence par le slow query log et par l’analyse des requêtes les plus fréquentes (pas seulement les plus lentes). Une requête à 30 ms exécutée 100 fois dans une page produit devient 3 s.
Un autre piège : certains ralentissements ressemblent à “une requête lente”, alors que le vrai problème est une attente de lock (transactions concurrentes sur des tables sollicitées). Dans ce cas, une requête “simple” peut passer de 5 ms à 500 ms selon la concurrence. Indices typiques : latence très variable, pics corrélés aux opérations panier/commande, et p95/p99 qui dérapent sans changement de code.
Ensuite, vous revenez aux fondamentaux InnoDB : mémoire, I/O, et plan d’exécution. Une boutique avec 10–50 Go de données et un buffer pool ridiculement petit va faire du read disk en continu et dégrader la latence. Sur MariaDB/MySQL, dimensionnez innodb_buffer_pool_size (souvent 60–75% de la RAM dédiée DB sur serveur DB isolé), surveillez les hits, et mettez en place une réplication si vous séparez lecture/écriture (pas toujours simple avec PrestaShop et les écritures “partout”).
Checklist “DB orientée TTFB” (pragmatique) :
- activer et exploiter slow query log (avec un seuil utile, et une rotation propre),
- repérer les requêtes les plus appelées par page (produit/catégorie),
- vérifier les index des colonnes dans
WHERE,JOIN,ORDER BY, - réduire les écritures inutiles côté front (modules stats/logs trop verbeux),
- planifier une routine de maintenance (tables qui grossissent, purge de données obsolètes).
Pour l’optimisation SQL, ne “devinez” pas : faites des EXPLAIN, corrigez vos index, et supprimez les requêtes inutiles (souvent injectées par des modules). L’article interne Index MySQL : optimiser WHERE, JOIN et ORDER BY avec EXPLAIN donne une méthodologie et des exemples directement exploitables : Index MySQL — EXPLAIN. Et pour la maintenance (tables qui grossissent, logs, nettoyage des paniers), évitez que la DB se dégrade mécaniquement : Routine de maintenance DB PrestaShop.
Viser < 200 ms : sans full-page cache côté anonyme, vous vous tirez une balle dans le pied
Soyons directs : PrestaShop n’embarque pas un full-page cache robuste dans le cœur. Vous pouvez améliorer Smarty, activer CCC, optimiser PHP/SQL, mais sur un gros catalogue + modules, le rendu HTML restera coûteux. Si votre objectif est “TTFB < 200 ms” sur les pages SEO critiques (home/catégories/produits) pour les visiteurs anonymes, la solution pragmatique est un reverse proxy cache (Varnish ou équivalent) et/ou un CDN avec caching HTML.
Avec Varnish, le point dur n’est pas l’installation, c’est la stratégie de cache : gestion des cookies, variation (langue/devise), invalidation (purge par tags), et fragmentation (ESI) pour garder des blocs dynamiques (panier, mini-compte) tout en servant le reste en cache. Si vous devez aller dans ce niveau de finesse, l’article interne sur Varnish + ESI (cache par fragments) est exactement le sujet.
Côté HTTP, votre objectif doit être de servir des réponses cacheables avec des en-têtes cohérents : Cache-Control, Surrogate-Control (si CDN), Vary maîtrisé, et suppression des cookies inutiles sur les pages publiques.
Points d’attention “PrestaShop réel” (là où beaucoup de projets échouent) :
- Cookies : un seul cookie posé globalement (A/B test, tracking, chat) peut casser le cache HTML pour tout le monde. Idéalement, les scripts de mesure ne doivent pas imposer un
Set-Cookiesur chaque page si vous visez le cache. - Variation : si vous variez par devise/langue, assurez-vous que la variation est explicite (et pas “par hasard” via des cookies fluctuants).
- Pages non cacheables : checkout, panier, compte, endpoints module doivent être sortis proprement du cache (sinon bugs).
- Invalidation : une stratégie “purge all” à chaque mise à jour produit peut faire s’effondrer le TTFB en pleine journée. Préférez la purge ciblée (tags) quand c’est possible.
Un anti-pattern fréquent sur PrestaShop : un module qui pose un cookie de tracking sur toutes les pages → cache bypass total → TTFB dépendant de PHP/SQL pour chaque visiteur. Dans ce cas, le “problème TTFB” est un problème fonctionnel/implémentation, pas un problème d’hébergement.
Plan d’action opérationnel (et critères de validation) pour tenir le TTFB sous 200 ms
Commencez par un protocole de mesure et un objectif réaliste. Exemple de contrat technique : p95 TTFB < 200 ms en cache-hit pour visiteurs anonymes sur home/catégorie/produit, mesuré depuis 2 régions, et p95 < 600 ms en cache-miss (valeur à ajuster selon complexité). Si vous n’annoncez pas cache-hit vs miss, vous ne pouvez pas piloter. Ajoutez une trace Server-Timing (app/db) pour corréler les régressions et rendre les discussions “module vs infra” factuelles.
Pour éviter l’objectif “200 ms” déconnecté du terrain, définissez aussi vos conditions de validation :
- p50 + p95, pas seulement une moyenne
- heures pleines (ex. 11h–14h / 18h–21h si votre trafic est retail), pas uniquement la nuit
- au moins 2 zones cohérentes avec vos clients (ex. France + un autre pays francophone, ou France + Canada si c’est votre réalité)
- même URL, même cache key (sinon vous comparez des choses incomparables)
Ensuite, appliquez un ordre d’exécution qui évite de micro-optimiser trop tôt :
- Stopper les hémorragies : désactiver modules non essentiels sur FO, vérifier qu’aucun hook ne fait d’appel réseau bloquant, corriger les erreurs PHP (warnings en prod = overhead + logs). Sur la gestion d’erreur et la config prod, voir Gestion d’erreur PHP — bonnes pratiques.
- Stabiliser l’infra : Nginx/Apache correctement tuné, keep-alive, TLS 1.3, logs de timing, FPM dimensionné, OPcache dimensionné.
- Débloquer la DB : slow logs, index, buffer pool, suppression des requêtes en boucle, maintenance. (Ne faites pas “juste augmenter les ressources” sans comprendre les verrous.)
- Mettre du cache page pour l’anonyme : Varnish/CDN, stratégie cookies/variation, invalidation. C’est ce qui vous donne structurellement un TTFB bas.
Enfin, validez avec des critères “prod-ready” et pas juste Lighthouse en local :
- (a) tests de charge modestes mais réalistes (ex. k6) pour vérifier que p95 ne s’effondre pas quand
pm.max_childrensature, - (b) suivi continu (APM + logs de timing) et alerte sur dérive (p95 TTFB,
upstream_response_time, erreurs 5xx), - (c) politique de déploiement qui évite les invalidations de cache catastrophiques à chaque release (warm-up, purge ciblée, fenêtres de déploiement).
Si vous êtes en train de migrer/moderniser, gardez en tête que PrestaShop 9 (Symfony 6.4, évolutions perf) peut réduire certains coûts structurels, mais ne supprimera pas la nécessité d’un cache HTML sur les pages publiques : PrestaShop 9 — nouveautés techniques.
L’objectif final n’est pas seulement “TTFB PrestaShop sous 200 ms” sur un graphe, mais un système qui reste rapide quand ça compte : pics marketing, périodes de soldes, campagnes Ads, et trafic multi-régions. En e-commerce, la meilleure optimisation est celle qui tient dans la durée, avec des mesures reproductibles et une stratégie de cache compatible avec vos contraintes fonctionnelles.




