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
| Solution | Type | Complexité | Cas d’usage principal |
|---|---|---|---|
| Varnish | Cache dédié | Moyenne | Cache HTTP à fort trafic |
| NGINX | Multi-usage | Faible-Moyenne | Proxy + cache tout-en-un |
| Squid | Forward/reverse | Haute | Proxies corporate, legacy |
| CDN | Réseau edge | Faible | Global, contenu statique |
| Redis | Applicatif | Moyenne | Contenu 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-7234 | Supporte Vary, request coalescing, stale cache-control |
| Invalidation programmatique | API REST complète (/souin) |
| Invalidation par tags | YKey (comme Varnish), Surrogate-Key |
| Bypass par cookie/header | Configurable |
| Stockage | Mémoire (Badger, Nuts) ou distribué (Olric, Etcd) |
| ESI | Edge 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é
- 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 stockage | Vitesse lecture/écriture |
|---|---|
| DDR4 RAM | 12 000 – 25 000 MB/s |
| DDR5 RAM | 18 000 – 30 000 MB/s |
| NVMe SSD | 3 000 – 7 000 MB/s |
| SATA SSD | 200 – 550 MB/s |
| HDD | 80 – 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
| Aspect | Mémoire | Disque |
|---|---|---|
| Latence | ~µs | ~ms (sans page cache OS) |
| Persistance | Perdu au redémarrage | Survit aux redémarrages |
| Coût | Cher par GB | Bon marché |
| Capacité | Limitée | Grande |
| Stabilité sous charge | Constante | Variable si I/O saturé |
4. Invalidation de cache programmatique
Varnish — Le plus complet
Varnish propose 4 méthodes d’invalidation :
| Méthode | Description | Usage |
|---|---|---|
| PURGE | Supprime un objet spécifique | curl -XPURGE http://example.com/page |
| BAN | Invalide par expression régulière | curl -XPURGE -H"X-Invalidate-Pattern: ^/admin" http://example.com/ |
| Soft Purge | Expire l’objet mais le garde en grace | Continuité de service pendant le refresh |
| Xkey/Ykey | Invalidation par tags/surrogate keys | Invalider 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.
| Option | Disponibilité | Capacités |
|---|---|---|
| ngx_cache_purge | Module tiers à compiler | Purge URI unique |
| proxy_cache_bypass | Natif | Refresh (pas de suppression) |
| Wildcard purge | NGINX Plus uniquement | Purge par pattern * |
| Lua-based | Avec module Lua | Headers 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
| Solution | Invalidation API | Invalidation par tags | Bypass cookie | Complexité |
|---|---|---|---|---|
| Varnish | PURGE/BAN | Xkey/Ykey | VCL | Moyenne |
| NGINX | Module requis | Non | Oui | Faible |
| NGINX Plus | Wildcards | Non | Oui | Faible |
| Souin | REST API | Surrogate-Key | Oui | Faible |
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
| Aspect | Varnish (xkey) | Souin |
|---|---|---|
| Header utilisé | xkey ou Surrogate-Key | Surrogate-Key |
| Stockage tags | En mémoire (index secondaire) | Configurable |
| Soft purge | xkey.softpurge() | Via stratégie CDN |
| Tags dynamiques | Automatique | Avec dynamic: true |
| Tags par config | Non (VCL manuel) | surrogate_keys: |
| API REST | Non (VCL custom) | Native /souin |
| Multi-CDN | Non | Akamai, Fastly, Cloudflare |
Bonnes pratiques de tagging
- Utiliser des préfixes sémantiques (
product-,user-,category-) pour éviter les collisions - Tagger au niveau le plus granulaire (ID produit) plutôt que générique
- 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
| CDN | Paramètres requis |
|---|---|
| Cloudflare | api_key, email, zone_id |
| Fastly | api_key, service_id |
| Akamai | hostname, network, service_id |
Signification de provider
| Valeur | Signification |
|---|---|
souin | Pas de CDN externe — Souin seul (défaut) |
cloudflare | Cloudflare est devant Souin |
fastly | Fastly est devant Souin |
akamai | Akamai est devant Souin |
Stratégies de purge
| Stratégie | Comportement |
|---|---|
hard | Supprime immédiatement le contenu du cache CDN |
soft | Marque 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ère | Varnish | NGINX | Souin (Traefik) |
|---|---|---|---|
| Invalidation API | PURGE/BAN natif | Module tiers requis | REST API native |
| Invalidation par tags | Xkey/Ykey | Non | Surrogate-Key |
| Bypass par cookie | Via VCL | Oui | Oui |
| Cache mémoire | Oui (seul mode) | Oui | Oui (Badger, Nuts) |
| Cache disque | Non (par défaut) | Oui | Via stockage distribué |
| Multi-CDN | Non | Non | Oui |
| Complexité | Moyenne (VCL) | Faible | Faible |
| Maturité | Très haute | Très haute | Plus récent |
| Écosystème Traefik | Non | Non | Natif |
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
- Souin — Plugin Traefik
- Documentation Souin
- GitHub darkweak/souin
- Souin Surrogate Keys
- Varnish Software — Cache Invalidation
- Varnish Software — Purge Tutorial
- Varnish Software — Secondary Keys
- GitHub varnish-modules/xkey
- Smashing Magazine — Cache Invalidation with Varnish
- NGINX — Content Caching Guide
- Baeldung — NGINX Clear Cache
- GitHub FRiCKLE/ngx_cache_purge
- BoldGrid — Comparing Disk, Redis, and Memcached
- DiskCache Benchmarks
- Surnia Ulula — Memcached vs Disk Cache
- ResearchGate — Memory vs Filesystem Cache Performance
- Traefik Plugin SimpleCache
- Traefik Community — Best Caching Option
- Traefik Enterprise — HTTP Cache