Table des matières :
- Périmètre, versions et prérequis (sans casser la prod)
- Nginx pour PrestaShop : vhost propre, TLS et règles de routage
- PHP-FPM : dimensionner le pool, éviter la contention et exploiter OPcache
- MariaDB pour PrestaShop : InnoDB, buffers, logs et discipline sur les index
- Redis : cache applicatif, sessions, et réduction de la pression IO
- Vérifier, mesurer, monitorer : ce qui prouve que la config est bonne
Périmètre, versions et prérequis (sans casser la prod)
Cette configuration « serveur dédié PrestaShop » part sur une base réaliste en 2026 : Debian 12, Nginx 1.24+, PHP 8.2 (ou 8.3 si vos modules sont propres), MariaDB 10.11 LTS et Redis 7. Les exemples ciblent PrestaShop 9.x (Symfony 6.4 côté BO + legacy FO), en mode mono-boutique ou multiboutique, avec un trafic qui justifie un dédié (CPU/IO prévisibles, isolation, marge pour du cache et de l’observabilité). Pour le dimensionnement matériel, ne partez pas au pif : alignez RAM/IOPS sur le volume catalogue et le taux de requêtes (voir Serveur dédié PrestaShop : dimensionnement matériel pour 100 000 produits et Hébergement PrestaShop : mutualisé, VPS ou cloud managé, comment choisir).
Pour cadrer rapidement le périmètre (et éviter les “mais chez moi…”), voici une grille simple à valider avant d’optimiser :
| Composant | Objectif | Point de vigilance typique sur PrestaShop |
|---|---|---|
| Nginx | statique + TLS + routage | règles “deny” trop larges, redirections, tailles d’upload |
| PHP-FPM | concurrence contrôlée + OPcache | workers trop gourmands, files d’attente, timeouts import |
| MariaDB (InnoDB) | latence SQL stable | index modules, tables temporaires, IO sync |
| Redis | cache applicatif + sessions | absence de maxmemory, évictions, timeouts |
Pré-requis non négociables avant de toucher à Nginx/PHP-FPM/MariaDB : snapshot (ou sauvegarde bloc), dump logique de la base, et un moyen de rollback du vhost (Git sur /etc/nginx, ou au minimum etckeeper). Toute modification sur MariaDB peut impacter le verrouillage InnoDB et les temps de réponse : planifiez une fenêtre et activez une surveillance des latences (p95/p99) côté HTTP et SQL. Si vous n’avez pas déjà une base propre Debian/Kernel/sysctl, partez de l’article Serveur dédié PrestaShop : installation et optimisation Debian pour gros catalogue.
Deux précautions “prod” qui évitent les surprises :
- Staging représentatif : idéalement un clone (même version PrestaShop + mêmes modules) avec une base anonymisée. Les régressions perf viennent souvent d’un module ou d’un pattern SQL, pas du serveur.
- Stratégie de log & conformité : les logs d’accès contiennent des IP, et en pratique ce sont des données personnelles au sens européen (à minima “donnée permettant d’identifier indirectement”). Concrètement : limitez l’accès aux logs, définissez une rétention, et évitez d’y injecter des données de formulaire.
Enfin, clarifions un point : le cœur PrestaShop n’offre pas de cache full-page natif fiable pour un e-commerce (cookies, panier, règles de prix, personnalisation). Donc le gain de perf serveur vient d’un triptyque classique : (1) Nginx pour servir le statique et amortir TLS, (2) PHP-FPM correctement dimensionné et avec OPcache, (3) MariaDB indexée/tunée, plus Redis pour réduire l’IO et la pression CPU. Pour le flux exact Nginx → FPM (et où ça bloque), relisez PHP-FPM : comprendre le flux d’exécution avec Nginx et Apache.
Checklist “avant/après” (simple, mais efficace) :
- Temps de réponse p95 (pages anonymes + BO) avant changement, puis après.
- Taux d’erreurs (HTTP 5xx/4xx significatives) + présence de 504.
- MariaDB : volume de slow queries, + top 5 des requêtes par temps total.
- PHP-FPM :
listen queue, workers “maxed out”, slowlog non vide. - Redis :
evicted_keysdoit rester à 0 (ou au moins expliqué/maîtrisé).
Nginx pour PrestaShop : vhost propre, TLS et règles de routage
Nginx doit faire deux choses sans discussion : servir vite le statique et router proprement vers index.php (front) et admin*/index.php (back). Le projet Nginx rappelle son périmètre : “nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server.” (Nginx documentation). Sur PrestaShop, ça signifie : pas de .htaccess, donc vous portez vos règles Apache vers le vhost Nginx, et vous assumez la maintenance de ces règles.
Avant même les règles, deux réglages “terrain” reviennent souvent sur des boutiques FR/EU :
- Taille d’upload : imports CSV, images, modules… Un
client_max_body_sizetrop bas se traduit en 413 côté Nginx. - Horodatage : pour corréler Nginx ↔ PHP ↔ MariaDB (et échanger avec un support), alignez la timezone (ex.
Europe/Paris) au niveau système et gardez des logs explicites.
Exemple minimal (à adapter : server_name, root, chemin admin) :
server {
listen 443 ssl http2;
server_name shop.example.tld;
root /var/www/prestashop;
index index.php;
# TLS : alignez-vous sur Mozilla SSL Configuration Generator
# https://ssl-config.mozilla.org/
ssl_certificate /etc/letsencrypt/live/shop.example.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shop.example.tld/privkey.pem;
# Logs séparés (utile pour corréler avec PHP-FPM)
access_log /var/log/nginx/shop.access.log;
error_log /var/log/nginx/shop.error.log warn;
# Limite upload (imports, images, modules)
client_max_body_size 64m;
# Statique : cache navigateur + pas de PHP
location ~* \.(?:css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}
# Sécurité basique : évite les leaks de fichiers
location ~* \.(?:ini|log|tpl|twig|sql|bak|swp)$ { deny all; }
location ~* ^/(?:\.git|var/cache|app/cache)/ { deny all; }
# Empêche l'exécution de PHP dans des répertoires "upload-like" (à adapter à votre arborescence)
location ~* ^/(?:img|upload|download)/.*\.php$ { deny all; }
# Routing PrestaShop
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS on;
# Option hardening : refuse les chemins qui n'existent pas (évite certains patterns d'attaque)
try_files $fastcgi_script_name =404;
fastcgi_pass unix:/run/php/php8.2-fpm-shop.sock;
# timeouts : évite les 504 silencieux sur imports
fastcgi_read_timeout 120s;
}
}
En pratique, beaucoup d’équipes ajoutent un bloc HTTP → HTTPS (simple, SEO-friendly, réduit les incohérences) :
listen 80;+return 301 https://$host$request_uri;- et, si vous utilisez HSTS, faites-le progressivement (d’abord sans
preload) pour éviter de vous enfermer en cas d’erreur de certificat.
Trois points de friction récurrents : (1) les 403 sur des assets (permissions/try_files/deny all trop large), (2) les règles admin (URL d’admin renommée, redirections), (3) l’HTTPS (mixed content, redirections 301, HSTS). Pour diagnostiquer rapidement un 403 côté Nginx vs filesystem, gardez sous la main Erreur 403 Forbidden : causes et dépannage étape par étape. Et pour éviter de dégrader SEO et crawl avec des redirections incohérentes, appuyez-vous sur HTTPS SEO : bonnes pratiques, certificats SSL et audit technique.
Astuce très rentable côté diagnostic : logguez la latence amont. Sans surcharger, un format de log avec request_time et upstream_response_time permet d’identifier en 2 minutes si la lenteur vient de PHP-FPM ou du réseau/du disque :
- si
request_timeest élevé etupstream_response_timeest élevé → PHP/DB (ou queue FPM) - si
request_timeest élevé etupstream_response_timeest faible → souvent débit client, gros payload, ou statique mal servi
Option utile mais à manipuler avec prudence : micro-cache FastCGI pour les pages 100% anonymes (catégories, fiches produit), en bypass strict dès qu’un cookie PrestaShop ou un paramètre de panier apparaît. Le gain typique n’est pas « magique » : vous gagnez surtout sur le TTFB p95 en lissant les pics (ex. p95 de 900 ms → 250–400 ms sur des pages catalogue non personnalisées), mais vous devez tracer les exceptions (cookies, pages compte/checkout, modules). Ne faites pas ça sans tests de non-régression (prix, devises, taxes) et sans logs de fastcgi_cache_status.
Mini-schéma de prudence (principe, à adapter) : mettre en cache uniquement les requêtes GET/HEAD, sans querystring “variable”, et bypass dès qu’un cookie de session/panier existe. Sur PrestaShop, les cookies incluent généralement un cookie nommé PrestaShop-* : c’est un bon signal pour décider “pas de cache”.
PHP-FPM : dimensionner le pool, éviter la contention et exploiter OPcache
Sur serveur dédié PrestaShop, le goulot n’est pas « PHP » en général, c’est la concurrence mal réglée : trop de workers → swap/CPU thrash ; pas assez → file d’attente et latence. Dans PHP-FPM, le réglage clé est le couple pm/pm.max_children, plus l’observabilité (pm.status_path, slowlog). Si vous voulez revoir le chemin d’exécution exact, c’est documenté dans PHP-FPM : comprendre le flux d’exécution avec Nginx et Apache.
Exemple de pool dédié à une boutique (socket Unix, status, slowlog). Fichier : /etc/php/8.2/fpm/pool.d/shop.conf.
[shop]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm-shop.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 40
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 16
pm.max_requests = 800
request_terminate_timeout = 120s
request_slowlog_timeout = 3s
slowlog = /var/log/php8.2-fpm/shop.slow.log
pm.status_path = /fpm-status
ping.path = /fpm-ping
Deux compléments importants en prod :
- Protégez
fpm-status: c’est extrêmement utile, mais ce n’est pas public. Dans Nginx, exposez-le uniquement en local (ou via VPN/bastion) et logguez-le à part si besoin. pm.max_requestsest souvent sous-estimé : en e-commerce, un module qui “fuit” un peu de mémoire finit par créer des workers obèses. Redémarrer un worker toutes les ~500 à 2000 requêtes est un compromis courant.
Méthode de calcul pragmatique pour pm.max_children : mesurez la RSS moyenne d’un worker sur votre code et vos modules (ex. 80–140 MB sur PrestaShop 9 avec beaucoup de hooks), puis appliquez un plafond : (RAM_dispo - marge_OS - marge_DB - marge_cache) / RSS_worker. Sur un dédié 64 GB, si MariaDB prend 32 GB de buffer pool, Redis 4 GB, et que vous gardez 8 GB pour l’OS + page cache, il reste ~20 GB pour PHP. Avec 120 MB par worker → ~166 workers théoriques, mais en pratique vous bornerez plus bas (CPU, locks DB, latence) : 30–80 workers est souvent plus stable.
Mesure rapide (sur un pic de charge, pas à vide) :
- repérez le PID d’un worker PHP-FPM, puis observez plusieurs processus (
ps -o pid,rss,cmd -C php-fpm8.2) - prenez une médiane (pas le max) : une requête lourde en BO ou un import peut faire exploser un worker temporairement
Indicateurs qui montrent une contention FPM (sans outil complexe) :
fpm-status:listen queue> 0 de manière persistante- Nginx : des 504 alors que MariaDB est “OK”
- slowlog : les traces pointent souvent des appels IO (images, API) ou des requêtes SQL lentes
OPcache est obligatoire. Le manuel PHP est clair : “The OPcache extension improves PHP performance by storing precompiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request.” (PHP Manual, OPcache). Pour des recommandations de paramètres (taille mémoire, timestamps, interned strings), partez de PHP OPcache : paramètres recommandés pour optimiser les performances et ajustez avec vos métriques (hit rate, restarts, OOM). En prod, désactivez opcache.validate_timestamps (ou mettez-le très bas uniquement si vous déployez sans restart) et imposez un vrai process de déploiement (symlink release + reload FPM).
Point d’attention fréquent : activer OPcache “oui”, mais trop petit. Symptômes : opcache_get_status() (ou votre exporter) montre un taux d’occupation élevé, des “restarts” et parfois un hit rate qui chute lors des déploiements. Côté PrestaShop (beaucoup de classes + overrides + modules), un OPcache trop étriqué se paie cash sur le TTFB.
MariaDB pour PrestaShop : InnoDB, buffers, logs et discipline sur les index
PrestaShop est SQL-heavy dès que le catalogue grossit : combinaisons, prix spécifiques, facettes, multi-boutique. MariaDB doit être configurée pour InnoDB (quasi exclusivement) et pour minimiser les lectures disque aléatoires. La base : buffer pool dimensionné, redo log cohérent, et un tmpdir sur disque rapide (NVMe) si vous avez des requêtes qui débordent en tables temporaires.
Snippet de départ (à adapter, et surtout à valider avec mysqltuner/pt-variable-advisor). Fichier : /etc/mysql/mariadb.conf.d/60-shop.cnf.
[mysqld]
# Connexion locale rapide
skip-name-resolve
bind-address = 127.0.0.1
# InnoDB
innodb_buffer_pool_size = 32G
innodb_buffer_pool_instances = 8
innodb_log_file_size = 2G
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 1
# Temp / mémoire
tmp_table_size = 256M
max_heap_table_size = 256M
# Logs pour optimisation
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0.2
log_queries_not_using_indexes = 0
Le paramètre à ne pas choisir au hasard est innodb_flush_log_at_trx_commit. À 1, vous avez la durabilité stricte (fsync à chaque commit) et donc plus d’IO. À 2, vous gagnez souvent en latence mais vous acceptez une fenêtre de perte en cas de crash brutal. Sur un e-commerce, la réponse « correcte » dépend de votre stratégie : réplication + PITR, niveau de tolérance métier, et qualité de votre stockage. Si vous ne savez pas, restez à 1 et investissez dans de l’IO. Référence technique : MariaDB Knowledge Base — innodb_flush_log_at_trx_commit
Quelques garde-fous qui améliorent la stabilité sans “sur-tuner” :
skip-name-resolve: bon réflexe, mais assurez-vous que vos comptes MySQL n’utilisent pas de hostnames qui nécessitent une résolution DNS.- Buffer pool : inutile de le pousser à 90% de la RAM. L’OS a besoin de cache disque (binlogs, tables, fichiers), et PHP aussi.
- Tables temporaires :
tmp_table_size/max_heap_table_sizetrop hauts sur un serveur avec beaucoup de connexions peuvent faire grossir la conso mémoire “par surprise”. Sur PrestaShop, mieux vaut monter progressivement et vérifier si vos requêtes créent des temp tables sur disque.
Ensuite, l’optimisation réelle se fait via le slow query log et les index : le cœur + les modules génèrent des requêtes parfois sous-optimales (jointures larges, WHERE non sargables, sous-requêtes). Instrumentez, puis corrigez par ajout d’index ou refactor SQL (quand possible). Pour la méthode d’analyse, voir PrestaShop MySQL : analyser slow query log et optimiser index. Et si vous utilisez des triggers pour maintenir des agrégats/cohérences (cas avancés), faites-le en connaissance de cause : Triggers MySQL : automatiser la cohérence des données en production.
Mini-scénario typique (utile pour prioriser) : vous lancez une campagne (Soldes/Black Friday), le trafic monte, et soudain le FO “tient” mais le BO devient inutilisable. Souvent, ce n’est pas “le serveur” : ce sont quelques requêtes back-office (recherche commandes, stats, modules de feed) qui déclenchent des scans/tri coûteux. Un slow log bien exploité permet de sortir 2–3 index ciblés qui changent la donne, là où augmenter pm.max_children ne ferait qu’empirer la pression sur la base.
Discipline recommandée sur les index (surtout en multiboutique) :
- chaque ajout d’index doit être justifié par un EXPLAIN (ou l’équivalent) et un gain observé
- documentez les index “modules” (ils sautent parfois en désinstallation/réinstallation)
- testez les migrations : un index en trop peut ralentir les écritures (panier/commande), pas seulement accélérer la lecture
Redis : cache applicatif, sessions, et réduction de la pression IO
Redis est utile sur un serveur dédié PrestaShop quand il remplace des accès disque/DB répétitifs (cache objet) et qu’il rend les sessions moins coûteuses (surtout si vous avez plusieurs frontaux plus tard). La définition officielle est sans ambiguïté : “Redis is an in-memory data structure store, used as a database, cache, and message broker.” (Redis documentation). Sur PrestaShop, l’usage réaliste est cache (classe cache legacy) + parfois sessions PHP ; ne confondez pas avec un full-page cache.
Côté redis.conf, deux choix structurants : persistance (AOF/RDB) et politique d’éviction. Pour un Redis utilisé comme cache pur, vous pouvez désactiver la persistance (moins d’IO, redémarrage plus « propre ») et choisir maxmemory-policy allkeys-lru avec un maxmemory explicite. Exemple (local uniquement) :
bind 127.0.0.1 ::1
protected-mode yes
port 6379
maxmemory 4gb
maxmemory-policy allkeys-lru
# Cache pur (optionnel) :
appendonly no
save ""
Trois remarques pragmatiques :
- Local-only : si Redis n’a aucune raison d’être accessible depuis Internet, ne l’exposez pas. Le combo
bind 127.0.0.1+ firewall est le minimum. - Évictions : si
maxmemoryest trop bas, vous aurez des évictions (et donc du cache qui “sert à rien”). Simaxmemoryest absent, vous risquez l’OOM (et un incident au pire moment). - Sessions : stocker les sessions dans Redis améliore la latence et simplifie une future mise en cluster, mais acceptez qu’un restart Redis puisse déconnecter des clients (ce qui est souvent acceptable vs une latence dégradée).
Intégration PHP : installez l’extension php-redis (pas predis en prod si vous pouvez l’éviter), et vérifiez qu’elle est chargée pour FPM (php -m | grep redis). Pour les sessions :
; /etc/php/8.2/fpm/conf.d/zz-redis-sessions.ini
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?database=2&timeout=2.5&read_timeout=2.5&persistent=1"
Pour le cache PrestaShop, privilégiez la configuration via le back-office (Paramètres avancés → Performance → Cache) quand elle est disponible, car elle évite des edits fragiles en dur dans les fichiers (et donc des surprises en update). Si vous devez le faire en code (cas CI/CD strict), documentez la modification et testez chaque upgrade PrestaShop : certaines versions changent la manière dont le cache est bootstrapé. Et surtout : ne mettez pas Redis “en prod” sans limite mémoire (maxmemory) ; sinon vous laissez l’OS tuer Redis ou vous partez en swap, et votre latence explose au pire moment.
Un test simple après mise en place (en plus des métriques) : observez le ratio “hit/miss” sur un pic de trafic et vérifiez que le temps moyen des pages catalogue diminue sans augmentation de la charge MariaDB. Si Redis “marche” mais que la base continue de monter, vous avez probablement un cache mal branché (ou des invalidations trop agressives par un module).
Vérifier, mesurer, monitorer : ce qui prouve que la config est bonne
Une config « qui démarre » ne prouve rien. Sur serveur dédié PrestaShop, vous validez avec (1) charge synthétique (wrk/k6), (2) logs, (3) métriques. Exemple de test : wrk -t4 -c64 -d60s https://shop.example.tld/categorie (pages anonymes) et un scénario k6 sur parcours panier/checkout. Vous comparez TTFB, p95, taux d’erreurs, et côté SQL : temps moyen + p95 des requêtes lourdes. Si votre p95 s’améliore mais que le taux 500/504 augmente, votre pool FPM ou vos timeouts sont mal alignés.
Une séquence de vérification “30 minutes” (sans outil exotique) :
nginx -tpuis reload : syntaxe OK, pas de vhost “à moitié chargé”.php-fpm8.2 -t: validation de configuration FPM.curl -I https://shop.example.tld/: contrôle des 301/302, headers, cache statique.tail -f /var/log/nginx/shop.error.logpendant un import BO : repérage immédiat des 504/timeouts.mysqladmin -uroot -p processlist(ou équivalent) : requêtes bloquées, locks, “Copying to tmp table…”.
Pour le monitoring, partez sur du simple et actionnable. Netdata est rapide à poser et donne une vision CPU/RAM/IO/DB immédiatement : Netdata : activer et configurer les collectors pour monitoring temps réel. Si vous êtes déjà en stack Prometheus, structurez des alertes sur latence Nginx, busy PHP-FPM, InnoDB buffer pool hit rate, Redis evictions (et pas juste sur CPU) : Prometheus : configuration des alertes, métriques et requêtes PromQL.
Repères utiles (à adapter à votre boutique ; l’important est la dérive, pas la valeur “parfaite”) :
- Nginx : montée du
upstream_response_timep95 = piste PHP/DB - PHP-FPM :
max children reachedoulisten queuenon nul = concurrence trop forte / workers trop lourds - MariaDB : augmentation du temps total “slow queries” = index/module ou IO
- Redis :
evicted_keys> 0 = cache sous-dimensionné (oumaxmemory-policyinadaptée)
Enfin, ne laissez pas la sécurité « pour plus tard » : un serveur dédié PrestaShop exposé est une cible. À minima : durcissement SSH, mises à jour, WAF si besoin, headers cohérents, audit modules. Pour une approche structurée (SBOM, WAF, CI/CD), voir PrestaShop : sécuriser APIs et modules avec SBOM, WAF et CI/CD et, côté navigateur (CSP/XSS), XSS : checklist de durcissement PrestaShop, CSP et encodage contexte. Une config perf qui ouvre des surfaces d’attaque (exposition de /fpm-status, admin non restreint, backups web-accessibles) est objectivement une mauvaise config.




