fr

Proxy-cache HTTP : Varnish, NGINX, Souin — Guide complet

Comparatif des solutions de proxy-cache HTTP (Varnish, NGINX, Squid, Souin/Traefik, CDN), performances mémoire vs disque, invalidation programmatique et surrogate keys.

1. Les solutions de proxy-cache en 2026

Varnish Cache

  • Type : HTTP accelerator / reverse proxy cache
  • Idéal pour : Sites à fort trafic, applications riches en contenu
  • Points forts : VCL (Varnish Configuration Language) pour des règles de cache flexibles, cache mémoire extrêmement rapide, grace mode pour servir du contenu périmé pendant les pannes backend, support ESI (Edge Side Includes)
  • Limites : Mémoire uniquement par défaut (pas de persistance), VCL a une courbe d’apprentissage

NGINX (avec proxy_cache)

  • Type : Serveur web avec cache intégré
  • Idéal pour : Cache polyvalent combiné au load balancing
  • Points forts : Cache disque et mémoire, configuration simple, peut faire reverse proxy + cache + load balancer en un seul service, cache purge disponible (avec module ou NGINX Plus)
  • Limites : Moins spécialisé que Varnish, certaines fonctionnalités nécessitent NGINX Plus

Squid

  • Type : Forward/reverse proxy avec cache
  • Idéal pour : Forward proxy caching, environnements corporate
  • Points forts : Mature et éprouvé (depuis 1996), supporte HTTP, HTTPS, FTP, cache disque avec index mémoire, contrôle d’accès par ACL
  • Limites : Configuration complexe, écosystème vieillissant

HAProxy (avec cache)

  • Type : Load balancer avec cache basique (depuis v1.8)
  • Idéal pour : Quand HAProxy est déjà en place et qu’un cache simple suffit
  • Points forts : Cache mémoire intégré au load balancing
  • Limites : Fonctionnalités de cache bien plus limitées qu’une solution dédiée

Traefik (avec plugins/middleware)

  • Type : Reverse proxy cloud-native
  • Idéal pour : Environnements Kubernetes et conteneurs
  • Points forts : Plugins de cache disponibles (Souin notamment), intégration native avec les orchestrateurs de conteneurs
  • Limites : Le caching n’est pas intégré nativement, repose sur des plugins

CDN (Cloudflare, Fastly, AWS CloudFront, Akamai)

  • Idéal pour : Distribution globale, edge caching
  • Points forts : Distribution géographique, protection DDoS incluse, infrastructure managée
  • Limites : Coût à l’échelle, moins de contrôle, vendor lock-in

Redis / KeyDB

  • Type : Data store en mémoire utilisé comme cache applicatif
  • Idéal pour : Cache au niveau application, stockage de sessions
  • Points forts : Latence sub-milliseconde, structures de données flexibles, clustering et persistance
  • Limites : Nécessite une intégration applicative, ce n’est pas un proxy transparent

Comparatif rapide

SolutionTypeComplexitéCas d’usage principal
VarnishCache dédiéMoyenneCache HTTP à fort trafic
NGINXMulti-usageFaible-MoyenneProxy + cache tout-en-un
SquidForward/reverseHauteProxies corporate, legacy
CDNRéseau edgeFaibleGlobal, contenu statique
RedisApplicatifMoyenneContenu dynamique, sessions

2. Traefik : quel plugin de cache choisir ?

Souin — Le plus complet pour la Community Edition

Souin est le plugin le plus complet et activement maintenu pour Traefik CE.

FonctionnalitéSupport
Conformité RFC-7234Supporte Vary, request coalescing, stale cache-control
Invalidation programmatiqueAPI REST complète (/souin)
Invalidation par tagsYKey (comme Varnish), Surrogate-Key
Bypass par cookie/headerConfigurable
StockageMémoire (Badger, Nuts) ou distribué (Olric, Etcd)
ESIEdge Side Includes supporté

Exemple de configuration avec bypass par cookie :

http:
  middlewares:
    souin:
      plugin:
        souin:
          default_cache:
            ttl: 300s
          # Les requêtes avec certains cookies peuvent être exclues

Sources : Documentation Souin, GitHub darkweak/souin

SimpleCache — Simple mais limité

Le plugin officiel Traefik :

  • Simple à configurer
  • Pas d’invalidation programmatique (issue #5)
  • Pas de limite de ressources configurable
  • Maintenance limitée selon la communauté Traefik

Traefik Hub / Enterprise

La version commerciale inclut un middleware de cache intégré avec support complet.


3. Cache mémoire vs cache disque : performances

Vitesses théoriques

Type de stockageVitesse lecture/écriture
DDR4 RAM12 000 – 25 000 MB/s
DDR5 RAM18 000 – 30 000 MB/s
NVMe SSD3 000 – 7 000 MB/s
SATA SSD200 – 550 MB/s
HDD80 – 160 MB/s

Source : BoldGrid — Comparing Disk, Redis, and Memcached Caching

Benchmarks réels (résultats surprenants)

Les benchmarks montrent des résultats parfois contre-intuitifs.

Selon les benchmarks DiskCache :

Au 90e percentile de latence sous forte charge, DiskCache présente moins de la moitié de la latence de Memcached.

Une étude citée par Surnia Ulula montre :

Le cache disque offre plus du double des performances de memcached, mais seulement quand il y a assez de mémoire libre pour que l’OS cache les fichiers.

L’explication : le système d’exploitation utilise la RAM disponible comme page cache pour les fichiers fréquemment accédés. Un “cache disque” finit souvent en RAM via l’OS.

Pour les contenus inférieurs à 20 MB, selon une étude ResearchGate, les vitesses de lecture sont virtuellement identiques quel que soit le type de stockage.

Trade-offs

AspectMémoireDisque
Latence~µs~ms (sans page cache OS)
PersistancePerdu au redémarrageSurvit aux redémarrages
CoûtCher par GBBon marché
CapacitéLimitéeGrande
Stabilité sous chargeConstanteVariable si I/O saturé

4. Invalidation de cache programmatique

Varnish — Le plus complet

Varnish propose 4 méthodes d’invalidation :

MéthodeDescriptionUsage
PURGESupprime un objet spécifiquecurl -XPURGE http://example.com/page
BANInvalide par expression régulièrecurl -XPURGE -H"X-Invalidate-Pattern: ^/admin" http://example.com/
Soft PurgeExpire l’objet mais le garde en graceContinuité de service pendant le refresh
Xkey/YkeyInvalidation par tags/surrogate keysInvalider tous les objets tagués “product-123”

Configuration VCL requise :

acl purge {
    "localhost";
    "192.168.55.0"/24;
}

sub vcl_recv {
    if (req.method == "PURGE") {
        if (client.ip !~ purge) {
            return (synth(405, "Method Not Allowed"));
        }
        return (purge);
    }
}

Sources : Smashing Magazine — Cache Invalidation Strategies, Varnish Software — Purge Tutorial

NGINX — Module externe nécessaire

NGINX ne supporte pas le purge nativement.

OptionDisponibilitéCapacités
ngx_cache_purgeModule tiers à compilerPurge URI unique
proxy_cache_bypassNatifRefresh (pas de suppression)
Wildcard purgeNGINX Plus uniquementPurge par pattern *
Lua-basedAvec module LuaHeaders backend pour invalidation

Exemple avec ngx_cache_purge :

curl -I myapp.example.com/api/ping -XPURGE

Sources : NGINX — Content Caching Guide, Baeldung — NGINX Clear Cache

Souin (Traefik) — API REST moderne

L’API Souin expose /souin :

# Purge par clé exacte
curl -XPURGE http://traefik/souin/cache/my-key$

# Purge par regex
curl -XPURGE http://traefik/souin/cache/my-pattern.*

# Purge par Surrogate-Key (tag)
curl -XPURGE -H "Surrogate-Key: product-123" http://traefik/souin

L’invalidation CRUD automatique est aussi supportée : les requêtes POST/PUT/PATCH/DELETE invalident automatiquement le cache GET correspondant.

Source : GitHub darkweak/souin

Comparatif invalidation

SolutionInvalidation APIInvalidation par tagsBypass cookieComplexité
VarnishPURGE/BANXkey/YkeyVCLMoyenne
NGINXModule requisNonOuiFaible
NGINX PlusWildcardsNonOuiFaible
SouinREST APISurrogate-KeyOuiFaible

5. Les surrogate keys en détail

Le concept : tags vs URLs

Les surrogate keys créent une couche d’indirection entre les URLs cachées et des identifiants métier (product-123, article-456). Au lieu de connaître toutes les URLs où apparaît un produit, on invalide simplement le tag product-123 et toutes les pages associées sont purgées.

Sans surrogate keys : un produit #166412 apparaît sur /products/166412, /category/electronics, /search?q=phone, /homepage. Il faut connaître et purger chaque URL individuellement — complexe et fragile.

Avec surrogate keys : chaque page est taggée avec les IDs des ressources qu’elle affiche. Une seule requête purge tout.

┌─────────────────────────────────────────────────────────────────┐
│                    STOCKAGE CACHE                               │
├─────────────────────────────┬───────────────────────────────────┤
│  URL                        │  Surrogate Keys (tags)            │
├─────────────────────────────┼───────────────────────────────────┤
│  /products/166412           │  product-166412                   │
│  /category/electronics      │  product-166412, product-8899     │
│  /search?q=phone            │  product-166412, product-234323   │
│  /homepage                  │  product-166412, promo-summer     │
└─────────────────────────────┴───────────────────────────────────┘

PURGE product-166412  →  Invalide les 4 URLs en une requête

Comment les pages sont taggées

Le backend applicatif ajoute un header HTTP dans la réponse pour indiquer les tags associés à cette ressource. Le cache stocke l’association URL / tags, et le backend les génère en fonction des données affichées :

┌──────────┐         ┌─────────────┐         ┌─────────────┐
│  Client  │ ──GET─► │   Cache     │ ──GET─► │   Backend   │
│          │         │  (Varnish/  │ ◄────── │ Application │
│          │ ◄────── │   Souin)    │         │             │
└──────────┘         └─────────────┘         └─────────────┘
                           │                       │
                           │    Réponse avec       │
                           │    header tags        │
                           │◄──────────────────────│
                           │                       │
                     Le cache stocke              Le backend génère
                     l'association                les tags basés sur
                     URL ↔ tags                   les données affichées

Workflow complet : de la mise en cache à l’invalidation

Etape 1 — Mise en cache d’une page produit

┌────────────────────────────────────────────────────────────────────────┐
│  Client ──GET /products/123──► Cache ──GET──► Backend                  │
│                                   │              │                     │
│                                   │   ◄──────────┤                     │
│                                   │   200 OK                           │
│                                   │   Surrogate-Key: product-123       │
│                                   │   {"name": "iPhone", ...}          │
│                                   │                                    │
│                              Stocke:                                   │
│                              /products/123 → [product-123]             │
└────────────────────────────────────────────────────────────────────────┘

Etape 2 — Une autre page référence le même produit

┌────────────────────────────────────────────────────────────────────────┐
│  Client ──GET /category/phones──► Cache ──GET──► Backend               │
│                                      │              │                  │
│                                      │   ◄──────────┤                  │
│                                      │   200 OK                        │
│                                      │   Surrogate-Key: product-123    │
│                                      │                  product-456    │
│                                      │                  category-phones│
│                                      │                                 │
│                                 Stocke:                                │
│                                 /category/phones → [product-123,       │
│                                                     product-456,       │
│                                                     category-phones]   │
└────────────────────────────────────────────────────────────────────────┘

Etape 3 — Invalidation après mise à jour en BDD

┌────────────────────────────────────────────────────────────────────────┐
│  Admin ──PUT /admin/products/123──► Backend ──webhook──► Cache         │
│                                         │                   │          │
│                                    Update BDD          PURGE           │
│                                                    Surrogate-Key:      │
│                                                    product-123         │
│                                                         │              │
│                                                    Invalide:           │
│                                                    - /products/123     │
│                                                    - /category/phones  │
└────────────────────────────────────────────────────────────────────────┘

Varnish : header xkey

Le backend ajoute le header xkey dans chaque réponse HTTP :

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: public, s-maxage=3600
xkey: product-166412 category-electronics type-listing

Formats acceptés : tags séparés par espaces, par virgules, ou via plusieurs headers xkey.

Exemple Express.js :

app.get("/products/:id", async (req, res) => {
  const product = await Product.findById(req.params.id);
  const related = await product.getRelated();

  const tags = [
    `product-${product.id}`,
    `category-${product.categoryId}`,
    ...related.map((p) => `product-${p.id}`),
  ];

  res.set("xkey", tags.join(" "));
  res.set("Cache-Control", "public, s-maxage=3600");
  res.json(product);
});

Configuration VCL pour le purge par xkey :

vcl 4.1;
import xkey;

backend default { .host = "127.0.0.1"; .port = "8080"; }

acl purgers {
    "localhost";
    "10.0.0.0"/8;
}

sub vcl_recv {
    if (req.method == "PURGE") {
        if (client.ip !~ purgers) {
            return (synth(403, "Forbidden"));
        }
        if (req.http.xkey-purge) {
            set req.http.n-gone = xkey.purge(req.http.xkey-purge);
            return (synth(200, "Purged " + req.http.n-gone + " objects"));
        }
        if (req.http.xkey-softpurge) {
            set req.http.n-gone = xkey.softpurge(req.http.xkey-softpurge);
            return (synth(200, "Soft-purged " + req.http.n-gone + " objects"));
        }
    }
}

sub vcl_backend_response {
    # xkey est automatiquement lu depuis le header de réponse
    unset beresp.http.xkey;
}

Purge programmatique :

# Purger toutes les pages contenant le produit 166412
curl -X PURGE -H "xkey-purge: product-166412" http://varnish.local/
# HTTP/1.1 200 Purged 4 objects

# Soft purge (garde le contenu en grace pendant refresh)
curl -X PURGE -H "xkey-softpurge: product-166412" http://varnish.local/

Sources : GitHub varnish-modules/xkey, Varnish Software — Secondary Keys

Souin : header Surrogate-Key

Souin utilise le header standard Surrogate-Key :

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, s-maxage=300
Surrogate-Key: product-166412 category-electronics

Méthode 1 — Tags dynamiques depuis le backend (recommandé) :

http:
  middlewares:
    souin-cache:
      plugin:
        souin:
          default_cache:
            ttl: 300s
          cdn:
            provider: souin
            dynamic: true # Active les tags dynamiques du backend

Avec dynamic: true, Souin lit automatiquement le header Surrogate-Key des réponses backend et stocke les associations.

Méthode 2 — Tags définis par configuration :

http:
  middlewares:
    souin-cache:
      plugin:
        souin:
          default_cache:
            ttl: 300s
          surrogate_keys:
            products_tag:
              url: "/products/.*"
            api_json:
              headers:
                Content-Type: "application/json"

Purge programmatique :

# Purger par surrogate key
curl -X PURGE -H "Surrogate-Key: product-166412" http://traefik.local/souin

# Purger plusieurs tags
curl -X PURGE -H "Surrogate-Key: product-166412 category-electronics" http://traefik.local/souin

# Lister les clés en cache
curl http://traefik.local/souin

# Purger par regex
curl -X PURGE http://traefik.local/souin/cache/product-.*

Source : GitHub souin/surrogate, Documentation Souin

Comparatif Varnish vs Souin pour les surrogate keys

AspectVarnish (xkey)Souin
Header utiliséxkey ou Surrogate-KeySurrogate-Key
Stockage tagsEn mémoire (index secondaire)Configurable
Soft purgexkey.softpurge()Via stratégie CDN
Tags dynamiquesAutomatiqueAvec dynamic: true
Tags par configNon (VCL manuel)surrogate_keys:
API RESTNon (VCL custom)Native /souin
Multi-CDNNonAkamai, Fastly, Cloudflare

Bonnes pratiques de tagging

  1. Utiliser des préfixes sémantiques (product-, user-, category-) pour éviter les collisions
  2. Tagger au niveau le plus granulaire (ID produit) plutôt que générique
  3. Ne pas abuser du nombre de tags par page (impact mémoire)

6. Souin et l’intégration Multi-CDN

Souin peut agir comme point de contrôle centralisé qui propage automatiquement les invalidations de cache vers un CDN externe. Une seule API à appeler, Souin se charge de synchroniser le cache local et le cache CDN.

Le problème sans Multi-CDN

Avec un CDN devant Traefik, il y a deux caches à invalider séparément :

┌────────┐      ┌─────────────┐      ┌─────────────┐      ┌─────────┐
│ Client │ ───► │     CDN     │ ───► │   Traefik   │ ───► │ Backend │
│        │      │ (Cloudflare)│      │   + Souin   │      │         │
└────────┘      └─────────────┘      └─────────────┘      └─────────┘
                      │                     │
                   Cache 1              Cache 2
                   (Edge)               (Origin)

Quand un produit est mis à jour, il faut purger Souin via son API, puis purger le CDN via une API différente — deux appels distincts, deux APIs à connaître.

La solution avec Multi-CDN Souin

┌────────┐      ┌─────────────┐      ┌─────────────┐      ┌─────────┐
│ Client │ ───► │     CDN     │ ───► │   Traefik   │ ───► │ Backend │
│        │      │ (Cloudflare)│      │   + Souin   │      │         │
└────────┘      └─────────────┘      └─────────────┘      └─────────┘
                      ▲                     │
                      │                     │
                      └─────── API ─────────┘
                        Invalidation
                        automatique

Une seule requête PURGE vers Souin invalide les deux caches.

Configuration

http:
  middlewares:
    souin-cache:
      plugin:
        souin:
          default_cache:
            ttl: 300s
            stale: 86400s # Garde en stale 24h si backend down
          cdn:
            provider: cloudflare # akamai | fastly | cloudflare | souin
            api_key: "${CF_API_KEY}"
            email: "${CF_EMAIL}"
            zone_id: "${CF_ZONE_ID}"
            strategy: soft # soft = grace mode sur le CDN
            dynamic: true # Lit les tags du backend dynamiquement
          surrogate_keys: # Règles de tags additionnelles (optionnel)
            static_assets:
              url: "/static/.*"
            api_responses:
              headers:
                Content-Type: "application/json"

Le bloc cdn ne remplace pas le cache Souin. Il configure un CDN placé devant Souin. Souin reste le cache origin et orchestre l’invalidation des deux niveaux simultanément.

Les deux niveaux de cache en détail

Les surrogate keys sont stockées aux deux niveaux. Souin orchestre l’invalidation des deux en une seule requête :

┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│   Client ───► Cloudflare (CDN) ───► Traefik + Souin ───► Backend        │
│                    │                       │                             │
│               Cache EDGE              Cache ORIGIN                      │
│           (configuré via cdn:)    (toujours actif)                      │
│                    │                       │                             │
│               Stocke avec             Stocke avec                       │
│               Surrogate-Key           Surrogate-Key                     │
│                    │                       │                             │
└────────────────────┼───────────────────────┼────────────────────────────┘
                     │                       │
                     └───────────┬───────────┘

                      PURGE Surrogate-Key: product-123


                           Souin API

                    ┌────────────┴────────────┐
                    │                         │
                    ▼                         ▼
            Invalide cache            Appelle API Cloudflare
            local Souin               pour invalider l'edge

Cloudflare, Fastly et Akamai supportent nativement les surrogate keys via leurs propres headers (Cache-Tag pour Cloudflare, Surrogate-Key pour Fastly, Edge-Cache-Tag pour Akamai). Souin peut convertir automatiquement le header selon le CDN configuré.

Paramètres requis par CDN

CDNParamètres requis
Cloudflareapi_key, email, zone_id
Fastlyapi_key, service_id
Akamaihostname, network, service_id

Signification de provider

ValeurSignification
souinPas de CDN externe — Souin seul (défaut)
cloudflareCloudflare est devant Souin
fastlyFastly est devant Souin
akamaiAkamai est devant Souin

Stratégies de purge

StratégieComportement
hardSupprime immédiatement le contenu du cache CDN
softMarque comme “stale” — le CDN peut servir l’ancien contenu pendant le refresh (recommandé pour éviter les pics de charge backend)

Flux d’invalidation complet

┌─────────────────────────────────────────────────────────────────────────┐
│  Mise à jour produit en BDD                                             │
│                                                                         │
│  Admin ──PURGE──► Souin API                                             │
│                   Surrogate-Key: product-123                            │
│                        │                                                │
│                        ├──► 1. Invalide cache local Souin               │
│                        │                                                │
│                        └──► 2. Appelle l'API CDN automatiquement        │
│                                  │                                      │
│                                  ▼                                      │
│                             Cloudflare API                              │
│                             POST /zones/{zone}/purge_cache              │
│                             {"tags": ["product-123"]}                   │
│                                                                         │
│  Résultat : Cache origin ET edge invalidés en une requête               │
└─────────────────────────────────────────────────────────────────────────┘

Le cache origin et les 200+ points de présence edge sont invalidés. Sans cette intégration, des utilisateurs risquent de voir des données obsolètes depuis certaines régions.

En résumé, une architecture typique avec Multi-CDN Souin ressemble à ceci :

                    ┌─────────────────┐
                    │   Cloudflare    │  ← Cache edge (Paris, NYC, Tokyo...)
                    │   (CDN global)  │
                    └────────┬────────┘

                    ┌────────▼────────┐
                    │    Traefik      │  ← Cache origin (votre datacenter)
                    │    + Souin      │
                    └────────┬────────┘

                    ┌────────▼────────┐
                    │    Backend      │
                    │    (API)        │
                    └─────────────────┘

Quand un admin met à jour un produit, une seule requête PURGE invalide le cache Souin (origin) et les 200+ points de présence Cloudflare dans le monde.

Note : si Traefik est le seul point d’entrée (pas de CDN), provider: souin par défaut suffit.

Source : Documentation Souin — Configuration


7. Matrice de décision

CritèreVarnishNGINXSouin (Traefik)
Invalidation APIPURGE/BAN natifModule tiers requisREST API native
Invalidation par tagsXkey/YkeyNonSurrogate-Key
Bypass par cookieVia VCLOuiOui
Cache mémoireOui (seul mode)OuiOui (Badger, Nuts)
Cache disqueNon (par défaut)OuiVia stockage distribué
Multi-CDNNonNonOui
ComplexitéMoyenne (VCL)FaibleFaible
MaturitéTrès hauteTrès hautePlus récent
Écosystème TraefikNonNonNatif

En résumé : pour rester dans l’écosystème Traefik, Souin répond à tous les critères (invalidation, bypass, tags). Pour la solution la plus éprouvée et flexible en matière d’invalidation, Varnish reste la référence avec son système de bans et Xkey.


8. Sources