Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

⚡ Varnish – Le Guide Ultime

Deep Dive : Reverse Proxy, VCL (State Machine), Cache (HIT/MISS), Purge, Backends & VMODs.

1.1 Facile

1. C'est quoi Varnish ?

Un Reverse Proxy & Web Cache Accelerator. Cache en RAM (ultra-rapide).

Cache Reverse Proxy
1.2 Moyen

2. Architecture

Client -> Varnish (Port 80) -> Backend (Apache/Nginx, Port 8080).

Architecture Backend
1.3 Avancé

3. 🚀 Le VCL (Core)

Varnish Configuration Language. Un "langage" (pas un fichier config). State Machine.

VCL State Machine
1.4 Avancé

4. Sub: vcl_recv

La "porte d'entrée". Décide : pass (pas de cache) ou lookup (chercher).

vcl_recv pass
1.5 Avancé

5. Sub: vcl_hash (Clé)

Comment créer la "clé" de cache. Normaliser les URLs, gérer les Cookie.

vcl_hash Cache Key
1.6 Moyen

6. Cache : HIT / MISS

vcl_hit (trouvé !) vs vcl_miss (pas trouvé -> vcl_fetch).

vcl_hit vcl_miss
2.1 Avancé

7. Sub: vcl_backend_response

Réponse du backend (beresp). On fixe le TTL (beresp.ttl). On active le cache.

vcl_fetch TTL
2.2 Moyen

8. Sub: vcl_deliver

La "porte de sortie". Modifier les headers avant d'envoyer au client (X-Cache: HIT).

vcl_deliver X-Cache
2.3 Moyen

9. Invalidation (PURGE)

Invalidation ciblée. Méthode PURGE + acl (ACL) pour la sécurité.

PURGE Invalidation
3.1 Avancé

10. Invalidation (BAN)

Le "Lurker". Invalidation par Regex (ex: ban("req.url ~ /images/.*")).

BAN Regex
3.2 Avancé

11. Outils (Stats & Logs)

varnishstat (Hit Rate !), varnishlog (le "firehose", debug).

varnishstat varnishlog
3.3 Avancé

12. Addons (VMODs) & Liens

Les "plugins" de Varnish. vmod_cookie, vmod_http, vmod_std. Liens.

VMOD vmod_cookie
1.1 C'est quoi Varnish ?

Varnish est un "HTTP Cache Accelerator" (Accélérateur de Cache HTTP). C'est un Reverse Proxy (comme Nginx ou HAProxy) conçu *exclusivement* pour la mise en cache haute performance.

Son travail est de s'asseoir *devant* votre serveur web (Apache, Nginx, Tomcat) et de "mémoriser" les réponses aux requêtes HTTP.

La "Killer Feature" : Le Cache en RAM

La plupart des caches (comme Nginx) cachent sur *disque* (lent). Varnish stocke son cache (les "objets") en mémoire vive (RAM).
Résultat : Si une page est en cache, Varnish peut la servir en microsecondes, sans jamais "réveiller" votre application (PHP, Python, Java). C'est le moyen le plus rapide de servir du contenu statique/anonyme.

La Philosophie : VCL (Varnish Configuration Language)

Varnish n'est pas configuré avec un fichier .ini ou .conf. Il est configuré avec du code.
Le VCL (voir 1.3) est un langage (similaire au C) que Varnish "compile" en code machine C natif. Cela vous donne un contrôle *absolu* sur le "comment", "quoi", et "pour combien de temps" mettre en cache.

1.2 Architecture (Client -> Varnish -> Backend)

Varnish est un "Reverse Proxy", ce qui signifie qu'il se place *entre* l'utilisateur et votre serveur d'application ("backend" ou "origin").

Diagramme de Flux (Cache HIT vs Cache MISS)
(Internet)
    |
    | (Requête: GET /page.html)
    ▼
+----------------------+
| VARNISH (Serveur 1)  |  (Écoute sur Port 80/443)
| (Cache en RAM)       |
+----------------------+
    |
    | 1. Varnish regarde dans son cache (RAM)
    |
    +---- (A) Cache HIT (Trouvé !) ----+
    |                                 |
    ▼                                 ▼ (B) Cache MISS (Non trouvé)
(Client)                          +----------------------+
(Réponse en < 1ms)                | BACKEND (Serveur 2)  | (Apache/Nginx/Node.js)
                                  | (Écoute sur Port 8080)
                                  +----------------------+
                                      |
                                      | 2. Le Backend génère la page
                                      |
                                      ▼
(Client)                          (Varnish)
(Réponse en 300ms)                | 3. Varnish reçoit la page
    ^                             | 4. Varnish la stocke (RAM)
    |                             | 5. Varnish la renvoie au client
    +-----------------------------+
1.3 🚀 Le VCL (Varnish Configuration Language)

Le VCL est le cœur de Varnish. Ce n'est *pas* un fichier de configuration, c'est un langage de programmation (DSL).

Varnish convertit votre fichier .vcl en code C, le compile avec GCC, et le charge (dlopen) comme une librairie partagée (.so). C'est pourquoi Varnish est si rapide.

La "State Machine" (Machine à états)

Le VCL n'est pas linéaire. C'est une "machine à états". Vous ne définissez pas ce qu'il faut *faire*, vous définissez ce qu'il faut faire à *chaque étape* du cycle de vie de la requête.
Chaque étape est une "subroutine" (sub vcl_...).

Subroutine (Étape)Rôle (Question)Décision (return)
vcl_recv"Je viens de recevoir la requête (req)."pass (Ne pas cacher) ou lookup (Chercher).
vcl_hash(Si lookup) "Comment dois-je créer la clé de cache ?"hash (Clé créée).
vcl_hit"J'ai trouvé l'objet en cache !"deliver (Livrer).
vcl_miss"Je n'ai pas trouvé l'objet."fetch (Aller au backend).
vcl_pass(Si pass) "J'envoie la requête au backend (sans cacher)."fetch (Aller au backend).
vcl_backend_response"J'ai reçu la réponse du backend (beresp)."deliver (Mettre en cache et livrer).
vcl_deliver"Je m'apprête à livrer la réponse (resp) au client."deliver (Livrer).
1.4 Subroutine : `vcl_recv` (La Porte d'Entrée)

vcl_recv est la *première* étape VCL qui s'exécute pour *chaque* requête entrante. Elle reçoit l'objet req (Request).

Son rôle principal est de **décider quoi faire** et de **nettoyer** la requête.

Décisions (return)
  • return (pass); : "Ne pas cacher." (Bypass). À utiliser pour /admin, /login, ou les requêtes POST.
  • return (lookup); : (Défaut). "Chercher dans le cache."
  • return (pipe); : (Avancé). "Ouvrir un tunnel" (ex: WebSockets).
  • return (purge); : (Voir 9.1). "Invalider le cache."
Exemple (default.vcl)

Un vcl_recv typique sert à *empêcher* la mise en cache de contenu dynamique.

sub vcl_recv {
    # 1. Ne jamais mettre en cache les requêtes non-GET/HEAD
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # 2. Ne jamais mettre en cache le back-office ou le login
    if (req.url ~ "^/admin" || req.url ~ "^/login") {
        return (pass);
    }

    # 3. Ne jamais mettre en cache si l'utilisateur a un cookie de session
    #    (Le "Cookie" est le pire ennemi du cache !)
    if (req.http.Cookie ~ "sessionid") {
        return (pass);
    }
    
    # 4. (Optionnel) Nettoyer les cookies analytiques (inutiles)
    #    (Le 'vmod_cookie' est un plugin, voir 3.3)
    #    import cookie;
    #    cookie.filter_except("sessionid");

    # 5. Si tout est OK, chercher dans le cache
    return (lookup);
}
1.5 Subroutine : `vcl_hash` (La Clé de Cache)

Si vcl_recv renvoie lookup, Varnish appelle vcl_hash. Son but est de construire la clé de cache (le "Hash").

Par défaut, Varnish utilise l'URL et le Host pour créer la clé.

Le Problème : Les "Cache Busters" inutiles

Vos outils marketing (Google Analytics, etc.) ajoutent des "query params" (?utm_source=...) à vos URLs.
/page.html?utm_source=google
/page.html?utm_source=facebook

Pour Varnish, ce sont deux URLs différentes. Il va stocker deux copies de la *même page*, polluant le cache (Cache Pollution).

La Solution : Nettoyer l'URL *avant* le hash

On "nettoie" l'URL dans vcl_recv (pour que vcl_hash utilise l'URL propre), ou on personnalise vcl_hash.

sub vcl_recv {
    # 1. Nettoyer les query params inutiles (utm_, gclid, etc)
    #    (Garde l'URL "propre" avant qu'elle n'arrive au hash)
    if (req.url ~ "(\?|&)(utm_|gclid|fbclid)=") {
        # 'regsub' (Regex Substitution) : 
        # Enlève le param et tout ce qui suit
        set req.url = regsub(req.url, "(\?|&)(utm_|gclid|fbclid)=.*", "");
    }
    
    return (lookup);
}

sub vcl_hash {
    # 2. (Par défaut)
    hash_data(req.url);
    hash_data(req.http.Host);
    
    # 3. (Optionnel) Gérer le 'Vary' (ex: Cacher version Gzip/non-Gzip)
    if (req.http.Accept-Encoding) {
        hash_data(req.http.Accept-Encoding);
    }
    
    return (lookup);
}
1.6 Cache : HIT / MISS / FETCH

Après le lookup (recherche), Varnish sait si l'objet est en cache.

RésultatSubroutine AppeléeSignificationAction (return)
HITvcl_hitTrouvé ! L'objet est en cache (et n'est pas expiré).return (deliver); (Parfait ! On envoie au client).
MISSvcl_missNon trouvé. L'objet n'est pas en cache.return (fetch); (On doit aller le chercher au Backend).
HIT-FOR-PASS(vcl_hit)(Avancé) Trouvé, mais c'est un "marqueur négatif". Varnish *sait* que cette page ne *doit* pas être cachée (ex: vcl_fetch a renvoyé pass).return (pass); (On passe au Backend, on ne re-tente pas).
Exemple (vcl_hit)

On utilise rarement vcl_hit, sauf pour du débug ou des cas très spécifiques.

sub vcl_hit {
    # (Par défaut, il ne fait que 'return (deliver)')
    
    # On peut forcer un 'pass' si on veut (ex: revalider 1% des hits)
    # if (random() < 0.01) {
    #   return (pass);
    # }
    
    return (deliver);
}

sub vcl_miss {
    # (Par défaut, il ne fait que 'return (fetch)')
    return (fetch);
}
2.1 Sub: `vcl_backend_response` (ex-`vcl_fetch`)

(Note : Dans Varnish 2.x/3.x, cette sub s'appelait vcl_fetch. Elle a été renommée vcl_backend_response pour plus de clarté).

C'est la sub la plus importante (avec vcl_recv). Elle s'exécute *après* avoir reçu la réponse du Backend (serveur Apache/Nginx).

Elle reçoit l'objet beresp (Backend Response). Son rôle est de décider :
1. Est-ce qu'on *peut* cacher cette réponse ?
2. Si oui, pour *combien de temps* (TTL) ?

Exemple (default.vcl)
sub vcl_backend_response {
    # 1. Ne pas cacher les erreurs 5xx (serveur HS)
    #    (On active le "hit-for-pass": ne pas réessayer pendant 2min)
    if (beresp.status >= 500) {
        set beresp.ttl = 120s;
        return (deliver); // (Ne met pas en cache, mais crée un marqueur négatif)
    }

    # 2. Ne pas cacher si le backend envoie un 'Set-Cookie'
    #    (Signifie que c'est une session privée)
    if (beresp.http.Set-Cookie) {
        set beresp.ttl = 0s; # (Ne pas cacher)
        return (deliver);
    }
    
    # 3. Lire le TTL (Cache-Control) envoyé par le backend (ex: "Cache-Control: public, max-age=3600")
    #    (Varnish le fait automatiquement)
    
    # 4. OU Forcer un TTL si le backend ne sait pas
    if (beresp.ttl <= 0s) {
        set beresp.ttl = 1h; # Cacher 1 heure
    }
    
    # 5. Mettre en cache et livrer
    return (deliver);
}
2.2 Sub: `vcl_deliver` (La Porte de Sortie)

vcl_deliver est la *toute dernière* étape, juste avant d'envoyer la réponse (resp) au client final. C'est l'endroit parfait pour "nettoyer" les headers ou ajouter des informations de débug.

Headers de Débug (X-Cache)

C'est la pratique n°1 : ajouter un header pour savoir si le cache a fonctionné.

sub vcl_deliver {
    # 1. Ajouter le header X-Cache
    #    (Varnish 4+ utilise 'obj.hits' ; Varnish 3 utilisait 'resp.hits')
    
    if (obj.hits > 0) {
        # Si obj.hits > 0, c'était un HIT
        set resp.http.X-Cache = "HIT";
        set resp.http.X-Cache-Hits = obj.hits;
    } else {
        # Si obj.hits == 0, c'était un MISS (ou PASS)
        set resp.http.X-Cache = "MISS";
    }

    # 2. Nettoyer les headers internes
    #    (Le client n'a pas besoin de savoir quel serveur a répondu)
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    
    return (deliver);
}
2.3 Invalidation (PURGE)

Le Problème : Vous avez mis une page en cache (TTL 1 heure), mais l'article est modifié. Comment forcer la suppression du cache *maintenant* ?

Solution : PURGE. C'est une méthode HTTP (comme GET) qui dit à Varnish "trouve cet objet dans ton cache et supprime-le".

1. Définir une ACL (Access Control List)

PURGE est dangereux. On doit le protéger pour que *seul* notre serveur (ex: WordPress, Django) puisse l'appeler.

# /etc/varnish/default.vcl (en haut)

# 1. Définir une ACL (liste d'IPs autorisées)
acl purge_acl {
  "localhost";
  "127.0.0.1";
  "10.0.0.5"; // (L'IP de notre serveur applicatif)
}
2. Autoriser PURGE dans vcl_recv
sub vcl_recv {
    # 2. Vérifier si la méthode est "PURGE"
    if (req.method == "PURGE") {
        
        # 3. Vérifier si l'IP client est dans l'ACL
        if (client.ip ~ purge_acl) {
            # 4. Autorisé ! Invalider.
            return (purge);
        } else {
            # 5. Non autorisé
            return (synth(403, "Forbidden"));
        }
    }
    # ... (reste du vcl_recv)
}

3. Utilisation (côté backend) :

# (Le CMS, après avoir sauvé l'article, exécute cette commande)
$ curl -X PURGE http://127.0.0.1/mon-article-modifie
3.1 Invalidation Avancée (BAN)

Le Problème : PURGE est chirurgical (rapide, O(1)). Mais que faire si je veux vider "tout ce qui est dans /images/" ou "tout ce qui est taggé 'utilisateur_123'" ?

Solution : BAN. Un "ban" est une règle (ex: une Regex) que Varnish ajoute à une "liste noire".
PURGE supprime l'objet. BAN dit "considère cet objet comme expiré (obj.ttl=0) s'il matche la règle".

PURGE vs BAN
PURGEBAN
CibleUn objet (via son Hash/URL).Un *pattern* (Regex, header).
VitesseInstantané (O(1)).Lent (O(n)). Le "Lurker" doit scanner tout le cache.
Exemple (Vider toutes les images)
# 1. Autoriser "BAN" (similaire à PURGE)
sub vcl_recv {
    if (req.method == "BAN") {
        if (client.ip ~ purge_acl) {
            # (Syntaxe VCL 4+)
            ban("req.http.host == " + req.http.host + " && req.url ~ " + req.http.X-Ban-Url);
            return (synth(200, "Ban added"));
        } else {
            return (synth(403, "Forbidden"));
        }
    }
    # ...
}

# 2. Utilisation (côté backend)
# (Vide tout ce qui matche /images/.*)
$ curl -X BAN -H "X-Ban-Url: /images/.*" http://127.0.0.1/
3.2 Outils (varnishstat & varnishlog)

Varnish tourne sur la "mémoire partagée" (Shared Memory), ce qui le rend ultra-rapide. Il fournit des outils "plugin" pour lire cette mémoire.

varnishstat (Le Dashboard)

Lance un dashboard "live" dans votre terminal, montrant les compteurs de performance.

Les 2 Métriques Clés (Le "Hit Rate") :

  • MAIN.cache_hit : Nombre de requêtes servies par le cache (Rapide !).
  • MAIN.cache_miss : Nombre de requêtes envoyées au backend (Lent).

Objectif : Le "Hit Rate" (cache_hit / (cache_hit + cache_miss)) doit être le plus haut possible (> 90%).

varnishlog (Le "Firehose")

C'est l'outil de débug *absolu*. Il affiche *tout* ce que Varnish pense, ligne par ligne (VCL, headers, ...). C'est un "firehose" (trop d'infos).

On doit le filtrer avec -q (Query).

# 1. Voir TOUT (inbuvable)
$ varnishlog

# 2. Voir seulement les requêtes pour "/ma/page"
$ varnishlog -q 'ReqURL eq "/ma/page"'

# 3. Voir seulement les "miss"
$ varnishlog -q 'VCL_call eq "MISS"'

# 4. Suivre une seule session client (par son ID 'vxid')
$ varnishlog -g request -q 'ReqStart'
# (Trouver le 'vxid' (ex: 12345) de la requête)
$ varnishlog -g request -q 'Vxid eq 12345'
3.3 Addons (VMODs) & Liens

Les VMODs (Varnish Modules) sont les "plugins" (extensions) de Varnish. Ce sont des bibliothèques (.so) écrites en C qui ajoutent de nouvelles fonctions au VCL.

VMODs Inclus (Principaux)
VMODDescription
std (Standard)Fonctions utilitaires. (std.tolower(), std.log(), std.duration()).
cookieLe "plugin" indispensable pour manipuler les cookies. (Nettoyer, filtrer, parser).
httpPermet de faire des sous-requêtes HTTP (ex: http.get()) *depuis* le VCL.
Exemple (vmod_cookie)

Objectif : Nettoyer tous les cookies (__ga, __utm...) *sauf* le cookie de session (sessionid) pour améliorer le Hit Rate.

# 1. Importer le VMOD
import cookie;

sub vcl_recv {
    # 2. Si un cookie existe
    if (req.http.Cookie) {
    
        # 3. Utiliser le VMOD pour ne garder QUE 'sessionid'
        cookie.keep("sessionid");
        
        # 4. Si (après filtre) il ne reste plus de cookie...
        if (cookie.empty()) {
            # ...supprimer le header pour cacher l'objet
            unset req.http.Cookie;
        }
    }
    
    return (lookup);
}