Réduire le TTFB sur PrestaShop : cache, serveur et overrides

Réduire le TTFB sur PrestaShop : cache, serveur et overrides
Un TTFB élevé sur PrestaShop plombe vos CWV et votre ranking. Cache Smarty, FastCGI Nginx, OPcache, requêtes SQL lentes dans les overrides : le guide complet pour descendre sous les 200 ms.

Un TTFB supérieur à 800 ms sur une fiche produit PrestaShop, c'est souvent un signal Google classé "poor" dans les CWV — et un facteur de ranking qui joue contre vous. Cache mal configuré, requêtes SQL non optimisées, overrides PHP lourds : voici comment diagnostiquer et réduire le Time To First Byte sur PrestaShop.

Comprendre ce que mesure le TTFB

Le TTFB (Time To First Byte) est le temps écoulé entre la requête HTTP du navigateur et la réception du premier octet de la réponse serveur. Il se décompose en trois phases :

  • Temps de traitement réseau : DNS, TCP handshake, TLS negotiation
  • Temps d'attente serveur : PHP, MySQL, cache
  • Début de transfert : premier octet de la réponse HTML

Dans PrestaShop, c'est presque exclusivement le temps de traitement PHP + MySQL qui pose problème. Un serveur bien configuré avec un bon cache descend sous les 200 ms. Sans cache, 1 à 3 secondes sont courantes sur un catalogue de taille moyenne.

Mesurer le TTFB avec précision

Avant d'optimiser, mesurez. Plusieurs outils donnent des lectures différentes :

  • Chrome DevTools → Network → première requête HTML : colonne "Waiting (TTFB)" — le plus fiable pour du local
  • WebPageTest.org : mesure depuis des sondes externes, plus représentatif
  • curl -w "%{time_starttransfer}" : pour automatiser les mesures en CI
# Mesure TTFB via curl
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
  https://votre-boutique.com/produit.html

Le cache Smarty : premier levier

PrestaShop utilise Smarty comme moteur de templates. Par défaut, le cache Smarty est activé mais parfois mal configuré — notamment après des déploiements où le dossier var/cache/ n'a pas été vidé proprement.

Vérifier et activer le cache Smarty

Dans le back-office : Paramètres avancés → Performances. Activez :

  • Cache Smarty : oui
  • Mise en cache multi-boutique : non (sauf si vous avez réellement plusieurs boutiques)
  • Cache des fichiers PHP : "Recompiler les fichiers de templates à chaque accès" → non en production

Programmatiquement, dans un override ou un module :

<?php
// Forcer le cache Smarty en lecture uniquement
$this->context->smarty->setCaching(Smarty::CACHING_LIFETIME_SAVED);
$this->context->smarty->cache_lifetime = 3600; // 1 heure

Cache personnalisé dans un override de controller

Si vous overridez ProductController et ajoutez de la logique métier lourde, implémentez du cache applicatif :

<?php

class ProductControllerCore extends FrontController
{
    public function initContent(): void
    {
        $cacheKey = 'product_data_' . (int)$this->product->id . '_' . (int)$this->context->language->id;
        $cached = Cache::retrieve($cacheKey);

        if ($cached === false) {
            $cached = $this->buildExpensiveProductData();
            Cache::store($cacheKey, $cached, 3600);
        }

        $this->context->smarty->assign('custom_data', $cached);
        parent::initContent();
    }
}

Identifier les requêtes SQL lentes

Le TTFB explose souvent à cause de requêtes N+1 dans les overrides. PrestaShop propose un mode debug SQL natif.

Activer le debug SQL

Dans config/defines.inc.php (à ne jamais faire en production) :

<?php
define('_PS_DEBUG_SQL_', true);

En production, utilisez le slow query log MySQL :

# my.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0.5
log_queries_not_using_indexes = 1

Optimiser avec DBQuery

Si vous avez des requêtes personnalisées dans vos overrides, utilisez DbQuery et évitez les sous-requêtes imbriquées :

<?php

class MyProductRepository
{
    public function getProductsWithStock(int $categoryId, int $langId): array
    {
        $query = new DbQuery();
        $query->select('p.id_product, pl.name, sa.quantity')
              ->from('product', 'p')
              ->innerJoin('product_lang', 'pl', 'p.id_product = pl.id_product AND pl.id_lang = ' . $langId)
              ->innerJoin('stock_available', 'sa', 'p.id_product = sa.id_product AND sa.id_product_attribute = 0')
              ->innerJoin('category_product', 'cp', 'p.id_product = cp.id_product')
              ->where('cp.id_category = ' . $categoryId)
              ->where('p.active = 1')
              ->orderBy('p.position ASC');

        return Db::getInstance()->executeS($query);
    }
}

Ajoutez des index MySQL sur les colonnes filtrées fréquemment. Sur un catalogue de 10 000+ produits, un index manquant sur category_product.id_category peut coûter 500 ms par requête.

Cache HTTP avec Nginx

Le cache applicatif PrestaShop ne suffit pas si chaque requête PHP est quand même exécutée. Un cache HTTP Nginx en amont évite totalement l'exécution PHP pour les pages cachables.

Configuration FastCGI Cache

# nginx.conf — zone de cache globale
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=prestashop_cache:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

server {
    # ...

    set $skip_cache 0;

    # Ne pas cacher si l'utilisateur est connecté ou a un panier
    if ($http_cookie ~* "PrestaShop-[a-z0-9]+") {
        set $skip_cache 1;
    }

    # Ne pas cacher les pages admin et le checkout
    if ($request_uri ~* "/(admin|checkout|order|cart)") {
        set $skip_cache 1;
    }

    location ~ \.php$ {
        fastcgi_cache prestashop_cache;
        fastcgi_cache_valid 200 60m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        add_header X-FastCGI-Cache $upstream_cache_status;

        # ... reste de la config PHP-FPM
    }
}

Le header X-FastCGI-Cache: HIT vous confirme que Nginx sert la réponse sans toucher PHP. Le TTFB descend alors sous les 20-50 ms pour les pages en cache.

Optimiser PHP-FPM

PHP-FPM mal dimensionné crée de la contention — les workers attendent et le TTFB monte.

Configuration pool adaptée

; /etc/php/8.2/fpm/pool.d/prestashop.conf
[prestashop]
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

; OPcache — critique pour le TTFB
php_admin_value[opcache.enable] = 1
php_admin_value[opcache.memory_consumption] = 256
php_admin_value[opcache.max_accelerated_files] = 20000
php_admin_value[opcache.validate_timestamps] = 0

OPcache avec validate_timestamps = 0 est le réglage le plus impactant en production : PHP ne vérifie plus si les fichiers ont changé à chaque requête. Couplez-le avec un opcache_reset() dans votre script de déploiement.

Identifier les overrides qui pèsent lourd

Les overrides PrestaShop sont souvent la source de TTFB élevés car ils s'exécutent dans le chemin critique de chaque requête.

Profiler avec Symfony Stopwatch

PrestaShop 8+ inclut Symfony. Utilisez le Stopwatch pour mesurer vos overrides :

<?php

use Symfony\Component\Stopwatch\Stopwatch;

class MyHeavyOverride extends MyHeavyOverrideCore
{
    public function expensiveMethod(): array
    {
        $stopwatch = new Stopwatch();
        $stopwatch->start('myHeavyOverride');

        $result = parent::expensiveMethod();
        $result = array_merge($result, $this->addCustomData());

        $event = $stopwatch->stop('myHeavyOverride');

        if ($event->getDuration() > 100) {
            PrestaShopLogger::addLog(
                'myHeavyOverride trop lent: ' . $event->getDuration() . 'ms',
                2
            );
        }

        return $result;
    }
}

Lazy loading des données non critiques

Ne chargez pas en synchrone ce qui n'est pas nécessaire au premier rendu. Les avis clients, les produits similaires, les widgets peuvent être chargés en AJAX après le LCP :

<?php

// ❌ Tout charger dans initContent()
public function initContent(): void
{
    $this->context->smarty->assign([
        'reviews'          => $this->loadAllReviews(),     // 300ms
        'relatedProducts'  => $this->loadRelatedProducts(), // 200ms
        'crossSell'        => $this->loadCrossSell(),       // 150ms
    ]);
    parent::initContent();
}

// ✅ Seulement les données critiques au premier rendu
public function initContent(): void
{
    $this->context->smarty->assign([
        'product' => $this->getProductData(), // 50ms
    ]);
    parent::initContent();
}

Checklist TTFB PrestaShop

  • Cache Smarty activé et non invalidé à chaque requête
  • OPcache avec validate_timestamps = 0 en production
  • Slow query log actif pour détecter les requêtes > 500 ms
  • Index MySQL sur les colonnes filtrées dans vos requêtes custom
  • FastCGI Cache Nginx pour les pages publiques sans session
  • PHP-FPM correctement dimensionné (pm.max_children selon la RAM)
  • Overrides auditées avec un profiler, données non critiques en AJAX
  • TTFB cible : < 200 ms sur les pages en cache, < 800 ms sans cache

Conclusion

Réduire le TTFB sur PrestaShop n'est pas une action unique — c'est une combinaison de cache HTTP, cache applicatif, OPcache et audit SQL. Dans la plupart des projets, activer le FastCGI Cache Nginx et corriger 2-3 requêtes lentes dans les overrides suffit à passer sous les 200 ms. C'est ce que Google mesure dans ses CWV et ce qui fait la différence entre un site "needs improvement" et un site "good".

Besoin d'aide pour auditer les performances de votre boutique PrestaShop ? Contactez-moi pour en discuter !

Jonathan Le-Peru

Écrit par Jonathan Le-Peru

Développeur backend avec plus de 7 ans d'expérience, spécialisé dans la création de solutions e-commerce robustes avec Prestashop. Passionné par l'optimisation des performances et les bonnes pratiques de développement.