PrestaShop lent : diagnostic TTFB, bots et optimisation serveur

Guide pratique : mesurer le TTFB, isoler réseau/PHP‑FPM/DB, repérer les bots et appliquer optimisations serveur (OPcache, FPM, cache HTTP) sans casser le checkout.

Illustration de l'interface de gestion serveur avec des graphiques et des paramètres techniques.

Table des matières :

  1. PrestaShop lent : lire le TTFB comme un signal système (pas comme un score)
  2. Mesurer proprement : isoler réseau, serveur web, PHP-FPM et base de données
  3. PrestaShop lent à cause des bots : prouver, classifier, puis contenir
  4. Optimisation serveur : réduire le TTFB en supprimant l’attente (queueing) avant l’exécution
  5. Base de données : quand le TTFB est en réalité du temps MySQL/MariaDB
  6. Côté PrestaShop : cache, modules et hooks — les vrais multiplicateurs de latence
  7. Plan d’intervention en production : du TTFB au correctif, sans casser le checkout

PrestaShop lent : lire le TTFB comme un signal système (pas comme un score)

Sur une boutique PrestaShop « lente », le TTFB (Time To First Byte) est souvent le premier symptôme mesurable, parce qu’il capture la latence avant que le navigateur ne reçoive le premier octet de réponse HTML. Sur PrestaShop, ce délai agrège des phases très différentes : réseau (DNS/TCP/TLS), file d’attente côté serveur (accept queue, saturation PHP-FPM), exécution PHP (front controller, modules, hooks), accès MySQL/MariaDB et génération du rendu (Smarty / Twig selon contexte). Tant que tu ne sépares pas ces composantes, « améliorer le TTFB » revient à optimiser à l’aveugle.

Pour cadrer la définition, web.dev (Google) rappelle : « Time to First Byte (TTFB) measures the time between the request for a resource and when the first byte of a response begins to arrive » et fixe une cible pragmatique : « A good TTFB is under 0.8 seconds » (800 ms) pour une expérience correcte côté utilisateur. Source : web.dev — article TTFB. En e-commerce, viser < 200–300 ms sur une page cacheable est réaliste sur une infra dédiée/optimisée, mais sur une page non cacheable (panier/checkout) tu raisonnes plutôt en p95/p99 et en stabilité sous charge.

Deux précisions utiles (qui évitent des diagnostics faux) :

  • Un bon TTFB n’implique pas un site “rapide” : si le HTML arrive vite mais que le rendu est bloqué par JS/CSS, l’utilisateur souffre quand même. Ici, on se concentre volontairement sur la latence serveur (symptôme « PrestaShop lent » côté back).
  • Le TTFB dépend de l’endroit où tu mesures : une boutique hébergée en France et testée depuis un laptop en fibre à Paris ne donnera pas les mêmes chiffres que depuis un point de mesure en Allemagne, au Canada ou derrière un réseau mobile. Si ton trafic est majoritairement France/Belgique/Suisse, mesure depuis l’Europe de l’Ouest (et idéalement depuis 2–3 points) avant de conclure à « serveur lent ».

Pré-requis (pour les actions de diagnostic ci-dessous) : PrestaShop 8.1.x à 9.1.x, PHP 8.2/8.3, Nginx (ou Apache) et accès aux logs (root ou au minimum lecture). Risques : activer des logs verbeux, profiler en production ou changer des limites PHP-FPM peut dégrader temporairement la boutique ; fais-le idéalement sur un staging fidèle (voir : Staging PrestaShop : créer un environnement Docker fidèle à la production).

Mesurer proprement : isoler réseau, serveur web, PHP-FPM et base de données

Commence par mesurer sans navigateur, pour éviter le bruit JS/CSS :

curl -s -o /dev/null \
  -w 'dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
  https://www.example.com/

time_starttransfer est ton TTFB côté client. Si dns/tcp/tls sont déjà élevés, ton problème n’est pas PrestaShop mais la résolution DNS, la latence réseau, une terminaison TLS lente, ou un CDN mal configuré. Si au contraire le TTFB explose alors que réseau ≈ OK, tu dois instrumenter côté serveur.

Pour interpréter rapidement une mesure, cette lecture « en couches » fonctionne bien :

Signal dans curl Hypothèse la plus probable Vérification immédiate
dns élevé Résolveur lent / DNS mal configuré tester avec un autre résolveur, vérifier TTL, DNSSEC, etc.
tcp élevé Latence réseau / pare-feu / SYN backlog traceroute/mtr, vérifier congestion, firewall stateful
tls élevé Handshake lent / chain / OCSP / CPU vérifier ciphers, session resumption, charge CPU
ttfb élevé mais dns/tcp/tls bas Attente serveur (queueing) ou PHP/DB logs Nginx timing + status PHP-FPM
total élevé mais ttfb correct payload/streaming/slow client taille HTML, compression, débit client

Sur Nginx, active un log_format qui expose la latence upstream (PHP-FPM) et la latence totale. Exemple minimal :

log_format timing '$remote_addr - $host "$request" $status '
                  'rt=$request_time uct=$upstream_connect_time '
                  'uht=$upstream_header_time urt=$upstream_response_time '
                  'ua="$http_user_agent"';
access_log /var/log/nginx/access_timing.log timing;
  • rt = temps total Nginx.
  • uht/urt = temps jusqu’aux headers / réponse depuis PHP-FPM.

Si rt ≈ urt, le goulot est PHP (ou ce que PHP attend : DB, FS, API). Si rt >> urt, regarde plutôt Nginx (I/O disque, gzip/brotli, proxy, etc.). Ce simple différentiel évite 80% des fausses pistes.

Astuce “prod-safe” : au lieu de noyer le serveur de logs, garde une fenêtre courte (ex. 10–15 minutes) pendant un pic réel (campagne, retargeting, périodes type soldes), puis analyse. Tu peux aussi filtrer sur des endpoints critiques (homepage, catégorie, produit, recherche, panier) en gardant un échantillon.

Ensuite, descends d’un cran sur PHP-FPM. La surcharge la plus fréquente sur PrestaShop lent n’est pas « PHP trop lent », mais PHP-FPM saturé (process occupés, file listen.backlog, pm.max_children trop bas). Sur Debian, active le slowlog :

; pool www
request_slowlog_timeout = 2s
slowlog = /var/log/php8.3-fpm/www-slow.log
pm.status_path = /fpm-status
ping.path = /fpm-ping

Tu auras des backtraces quand une requête dépasse 2s, ce qui donne un point d’entrée concret (classe, fonction, module).

  • “max children reached” dans les logs PHP-FPM : c’est la signature d’un queueing (des requêtes attendent qu’un worker se libère).
  • /fpm-status (protégé par IP) : si listen queue > 0 de façon répétée, le TTFB augmente même si le code n’a pas changé.

Pour comprendre le flux Nginx ⇄ PHP-FPM, garde sous la main : PHP-FPM : comprendre le flux d’exécution avec Nginx et Apache.

PrestaShop lent à cause des bots : prouver, classifier, puis contenir

Avant d’optimiser, vérifie que ta boutique n’est pas juste en train de servir une armée de crawlers. Sur beaucoup de shops, les bots (moteurs, SEO tools, scrapers, scanners) représentent une part très importante des hits, et ils ciblent souvent des URLs coûteuses : facettes, recherche interne, pages avec paramètres, endpoints AJAX, module/*, etc. Les symptômes typiques : TTFB qui grimpe surtout sur des pages non cacheables, pics CPU sans hausse de conversions, et un access.log dominé par des UA « exotiques ».

Analyse brutale et efficace (Nginx access log) :

# Top User-Agents
awk -F'ua="|"' '{print $2}' /var/log/nginx/access_timing.log | sort | uniq -c | sort -nr | head

# Top URLs coûteuses (rt)
awk '{for(i=1;i<=NF;i++) if($i ~ /^rt=/){sub("rt=","",$i); rt=$i} print rt, $7}' \
  /var/log/nginx/access_timing.log | sort -nr | head

Pour rendre ça exploitable, cherche une corrélation simple :

  • les 10 IP/UA qui génèrent le plus de requêtes,
  • les chemins qui concentrent les plus gros rt/urt,
  • et la présence (ou non) de conversions sur les mêmes périodes.

Mini-scenario fréquent : un scraper cible /recherche?controller=search&s=... ou des URLs à facettes avec paramètres ; chaque requête déclenche du SQL + génération Smarty, sans cache utile (cookies, variations), et finit par saturer PHP-FPM. Résultat : même les clients réels voient leur TTFB monter sur le checkout.

L’objectif n’est pas de « bloquer Googlebot », mais d’identifier : (1) les bots légitimes, (2) les bots utiles (monitoring, partenaires), (3) le trafic toxique (scraping agressif, scanners). La documentation Google Search Central précise que Google adapte le rythme de crawl pour éviter de surcharger un site (crawl rate), et qu’un site ralenti/instable peut influencer ce comportement. Source : Google Search Central — overview of crawlers.

Contenir proprement = rate limiting + règles ciblées, pas un deny all au hasard. Exemple Nginx :

# 10 req/s par IP sur les pages dynamiques
limit_req_zone $binary_remote_addr zone=ps_dyn:10m rate=10r/s;

# Détection simpliste (à affiner) des endpoints coûteux
location ~* ^/(recherche|search|module/|ajax|cart|panier) {
  limit_req zone=ps_dyn burst=30 nodelay;
  try_files $uri $uri/ /index.php?$args;
}

Bonnes pratiques (souvent oubliées) :

  • Répondre vite aux bots… sans leur donner tout : pour certains patterns de scraping, un 429 (Too Many Requests) + Retry-After peut être plus propre qu’un 403/444, car il réduit la pression sans déclencher d’escalade automatique.
  • Ne pas confondre UA et identité : un bot toxique peut se faire passer pour un UA connu. En cas de doute, l’approche robuste est IP reputation + fréquence + comportement (chemins, paramètres, erreurs).
  • Traiter les paramètres URL : beaucoup de charge vient de combinaisons quasi infinies (?order=...&page=...&q=...). Si tu laisses indexer/crawler tout ça, tu payes en CPU/DB.

Trois points d’attention côté PrestaShop :

1) les facettes / filtres peuvent exploser le budget crawl et la charge serveur : vois Budget de crawl PrestaShop : maîtriser URLs de filtres et facettes.

2) si ta boutique est infectée, certains bots « internes » (exfiltration, backdoors) masquent leur UA et génèrent un bruit anormal ; dans le doute, corrèle performance et sécurité (cf. Skimmer Magecart PrestaShop : détecter et nettoyer une infection).

3) ne rate-limit pas à l’aveugle les callbacks de paiement (webhooks) ou API partenaires : mets en place des allowlists par IP et des règles par chemin (cf. Webhooks PayPal : vérifier l’authenticité des notifications serveur).

Optimisation serveur : réduire le TTFB en supprimant l’attente (queueing) avant l’exécution

Quand PrestaShop est lent, le gain le plus rapide se fait souvent sur l’attente avant même que PHP exécute du code : congestion au niveau Nginx, files d’attente PHP-FPM, contention CPU, ou I/O disque. La règle : si ton request_time Nginx augmente en même temps que le nombre de connexions actives et que php-fpm affiche max children reached, tu n’as pas un problème de micro-optimisation, tu as un problème de capacité/concurrence.

Sur PHP-FPM, vérifie pm = dynamic (ou ondemand selon usage) et dimensionne pm.max_children par rapport à la RAM réellement disponible. Une méthode pragmatique : mesurer la RSS moyenne d’un worker PHP en charge, puis max_children ≈ (RAM_dispo - marge_OS - marge_DB) / RSS. Exemple : si un worker consomme 120 Mo en moyenne (OPcache inclus) et que tu as 6 Go pour PHP, max_children ≈ 50. Si tu laisses MySQL sur la même machine, tu réserves explicitement le buffer pool (sinon la concurrence mémoire te tue le TTFB par swap).

  • pm.max_requests (ex. 500–2000) pour recycler les workers et limiter l’impact de fuites mémoire de certains modules/extensions.
  • des timeouts cohérents (Nginx fastcgi_read_timeout, PHP max_execution_time) pour que les requêtes “bloquées” ne monopolisent pas des workers pendant 60–120s en pleine charge.

Active et calibre OPcache : c’est un baseline, pas une option. Le manuel PHP décrit OPcache comme un mécanisme qui améliore les performances en conservant en mémoire partagée du bytecode précompilé. Source : PHP.net — OPcache. Sur PrestaShop 8/9, un profil classique :

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=60000
opcache.validate_timestamps=0 ; en prod avec déploiement atomique
opcache.jit=0 ; JIT rarement utile sur PrestaShop

Point GEO (pratique, pas “marketing”) : si tes clients sont majoritairement en France/Europe et que ton serveur est hors UE (ou très loin du point d’entrée CDN), tu peux avoir un TTFB “structurellement” plus haut à cause de la latence réseau, même si PHP est propre. Dans ce cas, mesure depuis une sonde proche de ta base clients, et vérifie l’emplacement du datacenter, du CDN et de la terminaison TLS (où se fait réellement le handshake).

Pour la pile web, garde en tête que les tutos « génériques PHP » ne tiennent pas compte des pics e-commerce (soldes, campagnes, bots). Si tu es sur Nginx + PHP-FPM, utilise une config cohérente avec l’architecture (keepalive, buffers, timeouts) et mesure systématiquement. Pour un point de départ solide (Nginx, PHP-FPM, cache, OPcache), tu peux croiser avec : Symfony 7 : déploiement PHP-FPM Nginx, cache et opcache et, côté infra PrestaShop, Serveur dédié PrestaShop : config Nginx PHP-FPM MariaDB Redis.

Base de données : quand le TTFB est en réalité du temps MySQL/MariaDB

Si upstream_response_time est élevé mais que PHP-FPM ne sature pas, la base de données devient suspecte. PrestaShop fait beaucoup de requêtes par page (catalogue, prix, stock, règles panier), et les modules ajoutent souvent des SELECT supplémentaires au pire endroit (hooks front). Le pattern classique : TTFB qui grimpe surtout sur pages produit/catégorie avec combinaisons, ou sur panier avec règles promotionnelles.

Première étape : activer et exploiter le slow query log (avec un long_query_time agressif, ex. 0.2–0.5s) et corréler avec les endpoints. Le cœur du diagnostic et l’optimisation d’index sont détaillés ici : PrestaShop MySQL : analyser slow query log et optimiser index. Tant que tu n’as pas le plan d’exécution (EXPLAIN) et les index, tu n’as pas un diagnostic.

Pour éviter de te tromper de “coupable”, vise une lecture en 3 questions :

1) Est-ce que la requête lente est fréquente ou rare ? (une requête à 800 ms mais appelée 2 fois/jour n’explique pas un TTFB p95 élevé).
2) Est-ce que la lenteur est CPU, I/O, ou lock ? (les locks InnoDB peuvent rendre une requête “simple” très lente).
3) Est-ce que c’est structurel (schéma/index) ou conjoncturel (cache froid, buffer pool trop petit) ?

Sur MariaDB/InnoDB, les réglages « qui bougent vraiment l’aiguille » pour un shop monolithique : innodb_buffer_pool_size (doit absorber l’essentiel des données actives), innodb_log_file_size (écritures), et la latence disque (NVMe vs HDD). Sur une machine unique (web + DB), un buffer pool surdimensionné peut provoquer du swap et faire exploser le TTFB de façon non linéaire. Si tu dois toucher aux privilèges/transport DB (TLS, comptes, firewall), fais-le sans casser la prod : voir Sécurité MySQL : pare-feu, TLS, privilèges minimaux et audit.

Côté PrestaShop : cache, modules et hooks — les vrais multiplicateurs de latence

Le cœur de PrestaShop n’est pas « lent par nature », mais il est très sensible au cumul : modules qui s’empilent sur les hooks, requêtes répétées, et invalidations de cache agressives. Sur PrestaShop 8.1/9.1, la latence serveur vient souvent de (a) hooks front (header, displayNav, productFooter…), (b) surcharge du moteur de template (Smarty) quand le cache n’est pas correctement configuré, (c) IO disque sur le cache (fsync, inode, NFS), et (d) appels externes (tracking, recommandation, ERP) exécutés au runtime au lieu d’être asynchrones.

Action à fort ROI : auditer les modules par coût, pas par opinion. Tu cherches : temps par hook, nombre de requêtes par module, et fréquence d’exécution. Un angle efficace (sans “big bang”) consiste à :

  • reproduire la page lente (catégorie ou produit représentatif),
  • désactiver 1 module “front” à la fois sur staging,
  • remesurer urt (et idéalement le nombre de requêtes SQL),
  • et ne garder en backlog que les modules qui déplacent réellement le p95.

Pour les lenteurs applicatives (requêtes SQL, N+1, surcharge objet), tu peux t’appuyer sur : Optimisation de code PrestaShop : diagnostiquer lenteurs et requêtes SQL et, pour des optimisations plus « fonctionnelles » (pages produit/panier), Optimisation PrestaShop : accélérer pages produit et panier.

Ensuite, applique une stratégie de cache réaliste. Le cache Smarty/CCC/CDN règle surtout le front-end payload, pas le TTFB HTML — sauf si tu ajoutes un cache HTTP côté serveur (Varnish, Nginx fastcgi_cache) pour les pages publiques. Mais PrestaShop pose des cookies tôt, ce qui rend le cache HTTP « naïf » inefficace si tu ne varies pas correctement sur les cookies et si tu ne bypass pas le cache pour panier/compte.

  • Bypass cache dès qu’un cookie de session/connexion/panier est présent (ou dès qu’on est sur /panier, /commande, compte client).
  • Ne jamais cacher les pages qui affichent des prix personnalisés (B2B, groupes, règles panier) sans stratégie de variation robuste.
  • Surveiller le taux de hit cache et le TTFB p95 : un cache “présent” mais qui hit à 5% n’aidera pas.

Pour l’outillage natif/standard (Smarty cache, CCC, CDN), garde cette base : PrestaShop : optimiser performance via cache Smarty, CCC et CDN et, pour le choix et les contraintes d’un CDN en 2026 : CDN 2026 : critères techniques pour choisir le meilleur fournisseur.

Plan d’intervention en production : du TTFB au correctif, sans casser le checkout

Une remédiation propre d’un « PrestaShop lent » se fait en boucle courte : mesurer → hypothèse → changement minimal → mesure → rollback possible. Première passe (1–2h) : (1) activer logs timing Nginx, (2) extraire p95/p99 rt et urt par endpoint, (3) repérer si le coût vient de bots (UA / IP / chemins), (4) vérifier saturation PHP-FPM (max children reached, queue), (5) vérifier DB (slow log). Sans ces 5 points, tu ne sais même pas si tu dois toucher au code ou au serveur.

Si tu veux objectiver rapidement p95/p99 sur un échantillon (sans outillage lourd), tu peux exporter les rt= puis calculer des percentiles (selon tes outils disponibles). Exemple “brut” : extraire 10 000 dernières lignes et trier les temps ; ce n’est pas parfait mais suffisant pour une première passe et repérer une dérive.

Deuxième passe (demi-journée) : contenir la charge avant d’optimiser. Rate limiting ciblé, blocage des scrapers évidents, désindexation/normalisation des facettes qui génèrent des combinaisons infinies, et cache HTTP uniquement sur pages publiques strictement cacheables. C’est aussi le moment de vérifier que la stack est cohérente (versions PHP, opcache, pools FPM, buffers) et que tu n’es pas sur une offre inadaptée (mutualisé saturé, I/O bridée). Pour objectiver la partie hébergement (mutualisé vs VPS vs dédié vs cloud managé), la matrice de contraintes est ici : Hébergement PrestaShop : mutualisé, VPS ou cloud managé, comment choisir.

Troisième passe (1–2 jours) : traiter la cause racine. Soit tu identifies un module qui fait exploser les hooks et tu le remplaces/patches, soit tu optimises des requêtes (index, rewrite, cache applicatif), soit tu corriges une configuration serveur (FPM/DB) qui génère du queueing.

Pour rester “safe checkout”, garde cette règle opérationnelle : tout changement doit pouvoir être rollback (config versionnée, déploiement atomique, possibilité de désactiver une règle Nginx, une variation FPM, ou un module). Et planifie les interventions qui touchent au paiement (panier/commande/webhooks) sur un créneau à faible risque.

Monitorer est non négociable : mets des graphes CPU/RAM/IO, latence Nginx, état FPM, QPS DB. Si tu veux un collector prêt à l’emploi, vois : Netdata : activer et configurer les collectors pour monitoring temps réel. Un TTFB stable n’est pas un « bon réglage », c’est une discipline : capacité, contrôle des bots, et dette module sous surveillance continue.


À lire aussi