Sylius12 min

Sylius et Varnish : mettre en cache pour scaler votre e-commerce

Par Pierre-Arthur Demengel
SyliusVarnishCachePerformanceDevOps

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 :

  1. Varnish verifie si la reponse est en cache (dans la RAM)
  2. Si oui (cache hit) : la reponse est renvoyee directement en 1-5ms, sans toucher PHP
  3. 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 :

MetriqueSans VarnishAvec VarnishGain
TTFB page d'accueil280ms8msx35
TTFB fiche produit350ms12msx29
TTFB page categorie420ms10msx42
Requetes/seconde (ab)45 req/s2 800 req/sx62
CPU sous charge95%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.

Questions fréquentes

13 projets livrésGrand-Est & BelgiqueLighthouse >90Disponible immédiatement

Un projet en tête ?

Discutons de votre site web. Réponse garantie sous 24h.

Ou appelez directement :06 95 41 30 25

WhatsApp
Appeler