Sylius et Varnish : mettre en cache pour scaler votre e-commerce
La performance est un facteur critique pour un site e-commerce : chaque seconde de chargement supplementaire reduit le taux de conversion de 7% en moyenne. Varnish est l'arme secrete des sites a fort trafic. Ce reverse proxy HTTP peut servir des pages en quelques millisecondes au lieu de centaines de millisecondes. Dans ce tutoriel, je vous guide pas a pas dans la mise en place de Varnish avec Sylius.
Comment Varnish fonctionne
Varnish se positionne entre le client (navigateur) et votre serveur web (Nginx/Apache + PHP). Quand une requete arrive :
- Varnish verifie si la reponse est en cache (dans la RAM)
- Si oui (cache hit) : la reponse est renvoyee directement en 1-5ms, sans toucher PHP
- Si non (cache miss) : la requete est transmise au backend Sylius, la reponse est stockee en cache puis renvoyee
Le resultat : les pages publiques sont servies quasiment instantanement. PHP n'est sollicite que pour les premieres requetes et les pages privees.
Installation de Varnish
# Ubuntu/Debian
sudo apt-get install varnish
# Verifier la version (6.x ou 7.x recommandee)
varnishd -V
# Configuration du service
sudo systemctl edit varnish
# Modifier le port d'ecoute (80 par defaut pour Varnish)
# et definir le port du backend (ex: 8080 pour Nginx)
Architecture reseau
# Avant Varnish :
# Client -> Nginx (port 80) -> PHP-FPM -> Sylius
# Avec Varnish :
# Client -> Varnish (port 80) -> Nginx (port 8080) -> PHP-FPM -> Sylius
# Configuration Nginx : ecouter sur 8080 au lieu de 80
server {
listen 8080;
server_name maboutique.fr;
root /var/www/sylius/public;
# ... reste de la config identique
}
Configuration VCL pour Sylius
La VCL (Varnish Configuration Language) est le coeur de Varnish. Elle definit les regles de cache. Voici une configuration complete adaptee a Sylius :
# /etc/varnish/default.vcl
vcl 4.1;
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 5s;
.first_byte_timeout = 90s;
.between_bytes_timeout = 2s;
}
# Liste des IPs autorisees a purger le cache
acl purge {
"localhost";
"127.0.0.1";
}
sub vcl_recv {
# Gestion des requetes PURGE
if (req.method == "PURGE") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
return (purge);
}
# Gestion des requetes BAN (invalidation par pattern)
if (req.method == "BAN") {
if (!client.ip ~ purge) {
return (synth(405, "Not allowed."));
}
ban("req.url ~ " + req.http.X-Ban-Url);
return (synth(200, "Banned"));
}
# Ne pas cacher les pages admin
if (req.url ~ "^/admin") {
return (pass);
}
# Ne pas cacher le checkout et le panier
if (req.url ~ "^/(cart|checkout|order)") {
return (pass);
}
# Ne pas cacher l'API
if (req.url ~ "^/api") {
return (pass);
}
# Ne pas cacher si cookie de session Sylius present
if (req.http.Cookie ~ "_sylius") {
return (pass);
}
# Supprimer les cookies inutiles pour le cache
# (analytics, marketing, etc.)
if (req.http.Cookie) {
set req.http.Cookie = regsuball(req.http.Cookie,
"(^|;s*)(_ga|_gid|_gat|_fbp|_gcl)[^;]*", "");
# Si plus aucun cookie utile, supprimer le header
if (req.http.Cookie ~ "^s*$") {
unset req.http.Cookie;
}
}
# Normaliser l'Accept-Encoding
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} else {
unset req.http.Accept-Encoding;
}
}
return (hash);
}
sub vcl_backend_response {
# TTL par defaut : 1 heure
if (beresp.ttl <= 0s) {
set beresp.ttl = 1h;
}
# Pages produits : cache 2 heures
if (bereq.url ~ "^/products/") {
set beresp.ttl = 2h;
}
# Pages categories : cache 1 heure
if (bereq.url ~ "^/taxons/") {
set beresp.ttl = 1h;
}
# Assets statiques : cache 7 jours
if (bereq.url ~ "\.(css|js|png|jpg|jpeg|webp|gif|svg|woff2)$") {
set beresp.ttl = 7d;
}
# Ne pas stocker les reponses avec Set-Cookie
if (beresp.http.Set-Cookie) {
set beresp.uncacheable = true;
return (deliver);
}
# Grace mode : servir le contenu expire pendant 24h
# en cas de backend down
set beresp.grace = 24h;
return (deliver);
}
sub vcl_deliver {
# Ajouter un header pour debugger les hits/miss
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT (" + obj.hits + ")";
} else {
set resp.http.X-Cache = "MISS";
}
}
Invalidation du cache avec FOSHttpCacheBundle
Le cache Varnish doit etre invalide quand les donnees changent dans Sylius. Le FOSHttpCacheBundle fournit cette integration :
composer require friendsofsymfony/http-cache-bundle
# config/packages/fos_http_cache.yaml
fos_http_cache:
proxy_client:
varnish:
servers:
- '127.0.0.1:80'
base_url: 'https://maboutique.fr'
cache_control:
rules:
-
match:
path: ^/products
headers:
cache_control:
public: true
max_age: 7200
s_maxage: 7200
-
match:
path: ^/taxons
headers:
cache_control:
public: true
max_age: 3600
s_maxage: 3600
Invalidation automatique a la modification d'un produit
// src/EventSubscriber/CacheInvalidationSubscriber.php
namespace App\EventSubscriber;
use FOS\HttpCacheBundle\CacheManager;
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
final class CacheInvalidationSubscriber implements EventSubscriberInterface
{
public function __construct(
private readonly CacheManager $cacheManager,
) {}
public static function getSubscribedEvents(): array
{
return [
'sylius.product.post_update' => 'onProductChange',
'sylius.product.post_create' => 'onProductChange',
'sylius.product.post_delete' => 'onProductChange',
];
}
public function onProductChange(ResourceControllerEvent $event): void
{
$product = $event->getSubject();
// Purger la fiche produit
$this->cacheManager->invalidatePath(
'/products/' . $product->getSlug()
);
// Purger les pages categories associees
foreach ($product->getTaxons() as $taxon) {
$this->cacheManager->invalidatePath(
'/taxons/' . $taxon->getSlug()
);
}
// Purger la page d'accueil
$this->cacheManager->invalidatePath('/');
$this->cacheManager->flush();
}
}
ESI : les blocs dynamiques dans les pages cachees
Le probleme classique : vous voulez cacher une page produit, mais le header affiche le nombre d'articles dans le panier (specifique a chaque utilisateur). La solution : les ESI (Edge Side Includes).
Le principe : la page est cachee globalement, mais certains blocs sont remplaces par des "trous" que Varnish remplit dynamiquement.
{# templates/bundles/SyliusShopBundle/layout.html.twig #}
{# Le header avec le panier est un fragment ESI #}
{{ render_esi(controller('App\Controller\CartWidgetController::index')) }}
{# Le reste de la page est cache normalement #}
# Dans la VCL, activer ESI :
sub vcl_backend_response {
# Activer le traitement ESI
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
set beresp.do_esi = true;
}
}
Benchmarks : avant et apres Varnish
Voici les resultats mesures sur un projet Sylius reel avec un catalogue de 5 000 produits, serveur 4 vCPU / 8 Go RAM :
| Metrique | Sans Varnish | Avec Varnish | Gain |
|---|---|---|---|
| TTFB page d'accueil | 280ms | 8ms | x35 |
| TTFB fiche produit | 350ms | 12ms | x29 |
| TTFB page categorie | 420ms | 10ms | x42 |
| Requetes/seconde (ab) | 45 req/s | 2 800 req/s | x62 |
| CPU sous charge | 95% | 15% | -80% |
Les gains sont spectaculaires. Varnish transforme un serveur modeste en machine capable de supporter des milliers de visiteurs simultanes.
Integration avec un CDN
Pour aller encore plus loin, combinez Varnish avec un CDN (Cloudflare, Fastly, CloudFront). Le CDN met en cache les contenus au plus pres de vos visiteurs geographiquement :
- Cloudflare : le plus simple a mettre en place, plan gratuit disponible. Cachez les assets statiques et configurez les page rules pour les pages dynamiques.
- Fastly : base sur Varnish, donc votre configuration VCL est directement reutilisable. Ideal pour les gros volumes.
- CloudFront : si vous etes deja dans l'ecosysteme AWS.
L'architecture optimale pour un site a fort trafic : CDN (edge) -> Varnish (origin cache) -> Nginx -> PHP-FPM -> Sylius.
Pieges courants et troubleshooting
Les cookies qui empechent le cache
Le piege numero un. Un cookie Google Analytics ou Facebook Pixel suffit a empecher Varnish de cacher la page. La VCL doit nettoyer les cookies non essentiels.
Le cache qui ne s'invalide pas
Verifiez que le FOSHttpCacheBundle envoie bien les requetes PURGE. Ajoutez du logging pour tracer les invalidations. Utilisez varnishlog pour debugger.
# Voir les requetes en temps reel
varnishlog -g request -q "ReqMethod eq 'PURGE'"
# Statistiques du cache
varnishstat
Les formulaires CSRF qui cassent
Les tokens CSRF sont uniques par session. Si Varnish cache une page avec un formulaire, le token sera invalide pour les autres visiteurs. Solution : chargez les tokens via AJAX ou utilisez ESI pour les formulaires.
Conclusion
Varnish est un investissement en temps de configuration qui se rentabilise rapidement. Pour un site Sylius a trafic moyen a fort (1 000+ visiteurs/jour), c'est un incontournable. Les gains de performance ameliorent directement le SEO (voir notre guide SEO Sylius), l'experience utilisateur et le taux de conversion.
Besoin d'aide pour configurer Varnish sur votre Sylius ? Contactez-moi. J'interviens sur toute la stack de performance : Varnish, Redis, CDN, optimisation Doctrine et PHP. Decouvrez nos services d'optimisation et nos tarifs.
