source: spip-zone/_plugins_/varnish/vspip4.vcl

Last change on this file was 89894, checked in by pierre@…, 4 years ago

Fichier de configuration pour varnish4.

File size: 16.2 KB
Line 
1vcl 4.0;
2import std;
3#
4# Configuration de varnish (varnish-cache.org)
5# optimisée pour SPIP
6#
7# Varnish est un proxy inverse, installé sur le serveur Web,
8# qui permet :
9#
10# - 1. Rapidité. Absorbe une demande importante sur une même URL
11#      et y répond directement sans solliciter apache
12# - 2. Tolérance aux pannes. Renvoie des pages depuis sa mémoire cache
13#      même lorsque apache est planté
14# - 3. Souplesse dans l'affectation de serveur(s) apache à différentes
15#      applications (par ex. load balancing, changement de serveur, etc)
16# - 4. Moindre consommation de CPU : économique et écologique
17#
18# La configuration ci-dessous permet d'optimiser son fonctionnement
19# avec SPIP ; elle s'applique sans problème à des serveurs
20# hébergeant aussi d'autres types de scripts, qui peuvent utiliser
21# les mêmes mécanismes de communication X-Varnish-Message et X-Varnish-Purge
22#
23
24
25## -- BACKEND PRINCIPAL --
26##
27## Tout d'abord, nous allons définir l'adresse du serveur apache hébergeant
28## nos sites Web (ce que varnish appelle le backend).
29##
30## Dans notre cas, le serveur apache est accessible sur le
31## port 8080 de l'interface 127.0.0.1 ; il répond aux mêmes requêtes que
32## lorsqu'il est configuré sur le port 80 :
33##     GET / HTTP/1.0
34##     Host: nomdusite.tld
35##
36## Pour ce backend, nous définissons un temps de latence avant le premier octet
37## de 300s ; ainsi, lorsqu'une page demande un temps de calcul très long (par
38## exemple un POST d'un long article), on attend jusqu'à 5 minutes avant de
39## déclarer forfait et d'envoyer une erreur 500.
40## http://vincentfretin.ecreall.com/articles/varnish-guru-meditation-on-timeout
41##
42## De plus, varnish va lancer chaque seconde une requête de test sur une URL
43## type ; si 2 requêtes sur les 3 dernières sont en faute, il déclarera le
44## backend "malade" ("sick") et passera en mode "tolérance aux pannes",
45## jusqu'à ce que le backend revienne en bonne santé ("healthy").
46##
47backend default {
48        .host = "127.0.0.1";
49        .port = "8080";
50        .first_byte_timeout = 300s;
51        .probe = {
52                # Ici mettre un hit vers un petit fichier fixe sur le backend
53                #.url = "/";
54                .request =
55                        "GET /prive/images/searching.gif HTTP/1.0"
56                        "Host: zzz.rezo.net"
57                        "Connection: close";
58                .timeout = 34 ms; 
59                .interval = 3s; 
60                .window = 3;
61                .threshold = 2;
62        }
63        .max_connections = 50;
64}
65
66
67## -- BACKEND DE TEST D'ERREUR 503 --
68##
69## Ce backend "guru" est toujours en panne ; il est destiné à provoquer
70## une erreur 503, ce qui permet d'afficher délibérément le message d'erreur
71## qu'on définira en fin de ce fichier de configuration
72## => http://nomdusite.tld/I'm-a-guru
73##
74backend guru {
75        .host = "127.0.0.1";
76        .port = "8082"; # !!il faut choisir un numéro de port inutilisé
77}
78
79
80
81## -- RECV --
82## Cette fonction est appelée à chaque connexion d'un client sur varnish.
83##
84## Elle normalise la requête :
85## - en supprimant les cookies inutiles
86## - en unifiant les différents types de Accept-Encoding
87##
88sub vcl_recv {
89
90        ## Sélection du backend
91        ## set req.backend_hint = default;
92        ## set req.http.host = "zzz.rezo.net";
93
94        ## -- GRACE --
95        ##
96        ## La "grace" sert dans deux scénarios :
97        ##
98        ## - a. "Absorber" la connexion *simultanée* de plusieurs clients
99        ##      sur une même URL : le temps que le backend calcule et renvoie
100        ##      la nouvelle réponse, on s'autorise à servir une réponse en cache
101        ##      mais dont la date de péremption est dépassée de moins de 30s.
102        ## - b. "Panne" : si le backend est en panne, on s'autorise à servir
103        ##      des contenus mis en cache mais dont la date de péremption est
104        ##      dépassée (jusqu'à une heure).
105        ##
106        if (std.healthy(req.backend_hint)) {
107                set req.http.grace = "30s";
108        } else {
109                set req.http.grace = "1h";
110        }
111
112        ## -- COOKIES --
113        ##
114        ## On nettoie ici les cookies qui n'impactent pas SPIP ;
115        ## essentiellement les cookies de tracking statistique, mais aussi
116        ## les cookies d'option traités côté client (vs. côté serveur).
117        ## Attention les cookies importants côté serveur (cookie de session admin,
118        ## par exemple) ne doivent *pas* être nettoyés.
119        ##
120        if (req.http.Cookie) {
121
122                ## __utm[a-z] = cookies google analytics
123                ## xtvrn = cookies xiti
124                set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *(__utm[a-z]|xtvrn)=[^;]+;? *", "\1");
125
126                ## _pk.* = cookies piwik
127                ##   => attention, sur l'URL de piwik, ne pas nettoyer les cookies piwik
128                if (req.url !~ "/piwik\.php") {
129                        set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *(_pk_[^=]+)=[^;]+;? *", "\1");
130                }
131
132                ## dans une application particulière, le cookie "blink" est traité
133                ## côté client ; il ne nous intéresse pas, on le nettoie
134                set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *(blink|service_\w+)=[^;]+;? *", "\1");
135
136                ## si le cookie résultant est vide, le supprimer
137                if (req.http.Cookie == "") {
138                        unset req.http.Cookie;
139                }
140
141                ## si on est dans un repertoire statique ignorer totalement
142                ## les cookies (ici, les répertoires SPIP & Drupal + les images
143                ## css, scripts, etc.)
144                ## (le ?\d+ final est un éventuel timestamp)
145                if (req.url ~ "^[^?]*\.(css|js|jpg|jpeg|gif|png|ico|txt|mp3|ttf)(\?\d+)?$"
146                || req.url ~ "^/(local|IMG|extensions|plugins|static)/") {
147                        unset req.http.Cookie;
148                }
149
150        }
151        ## fin de la section COOKIE
152
153
154        ## -- X-FORWARDED-FOR --
155        ##
156        ## Ajouter un entête X-Forwarded-For: IP
157        ## en le concaténant avec un éventuel entête déjà existant
158        ##
159        if (req.http.x-forwarded-for) {
160                set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
161        } else {
162                set req.http.X-Forwarded-For = client.ip;
163        }
164
165        ## -- GZIP --
166        ## unifier les accept-encoding: accepte gzip ou pas ; on ne gere pas deflate
167        ## (notamment : FF annonce "gzip, deflate" et Safari "gzip,deflate" !)
168        if (req.http.Accept-Encoding) {
169                if (req.http.Accept-Encoding ~ "gzip") {
170                        set req.http.Accept-Encoding = "gzip";
171                #} elsif (req.http.Accept-Encoding ~ "deflate") {
172                #       set req.http.Accept-Encoding = "deflate";
173                } else {
174                        unset req.http.Accept-Encoding;
175                }
176        }
177
178        ## -- RANGE --
179        ## Varnish ne doit pas gérer pas les requêtes de contenu partiel ;
180        ## on les passe directement au backend
181        ## cf. http://forum.developers.facebook.net/viewtopic.php?id=68440#p253346
182        if (req.http.Range) {
183                return(pipe);
184        }
185
186        ## -- TEST 503 --
187        ## cf. ci-dessus le backend "guru" qui ne mène nulle part
188        ## se teste via le navigateur sur http://urldusite.tld/_-_-I-m-a-guru
189        ## permet d'afficher l'erreur définie en bas de ce fichier
190        ## le nom zen vient de l'erreur par défaut de Varnish : "guru meditation"
191        if (req.url == "/_-_-I-m-a-guru") {
192                set req.backend_hint = guru;
193        }
194
195
196        ## -- DIVERS --
197        ## certain robot fou demande host:127.0.0.1, on le jette immédiatement
198        ## note: si le client est local, on accepte (c'est pour munin)
199        if (req.http.host == "127.0.0.1" && client.ip != "127.0.0.1" ) {
200                return( synth(500,"Unknown virtual host."));
201        }
202
203}
204
205
206## -- FETCH --
207## Cette fonction est appelée à chaque retour du backend vers varnish.
208##
209
210sub vcl_backend_response {
211
212        ## -- REDIRECTION --
213        ## supprimer le port (:8080) envoyé par le backend lors d'une redirection
214        ## car on veut rediriger vers le port public :80
215        if (beresp.http.Location) {
216                set beresp.http.Location = regsub(beresp.http.Location, "^(\w+://[^/]+):\d+", "\1");
217        }
218
219        ## -- TTL: DUREE DE CACHE --
220        ##
221        ## C'est la durée de vie de la page dans le cache ; au-delà de cette durée,
222        ## elle ne pourra être servie au client que dans le scénario de "grace".
223        ##
224        ## Différents cas :
225        ## -1 la ressource signale X-Varnish-TTL: 20s
226        ##    (c'est ce que fait le plugin pour SPIP)
227        ##    elle annonce à varnish que son script lui signalera plus tard,
228        ##    par un entete X-Varnish-Purge, quand le moment sera venu de
229        ##    rafraichir la page. Dans ce cas de figure on peut donc la mettre
230        ##    en cache pour la durée indiquée (si elle est > 0)
231        ## -2a la ressource est statique, on la cache un certain temps raisonnable
232        ##     (sauf si un autre entete indique qu'elle n'est pas cachable)
233        ## -2b la ressource est dynamique, on ne la cache pas
234
235        ## L'entête X-VARNISH-TTL permet au backend de définir le ttl du cache
236        ## code inspiré de: http://open.blogs.nytimes.com/2010/09/15/using-varnish-so-news-doesnt-break-your-server/
237        ## http://www.lovelysystems.com/configuring-varnish-to-use-custom-http-headers/
238        ##
239        ## (n'utilise pas X-SPIP-Cache car, sur SPIP standard, ça tuerait les stats)
240        ##
241        if (beresp.http.X-VARNISH-TTL) {
242                set beresp.ttl = std.duration(beresp.http.X-VARNISH-TTL +"s",0s);
243                #unset beresp.http.X-VARNISH-TTL;
244        }
245        ## sinon se baser sur la logique habituelle de varnish (Expires, etc) ;
246        ## en ajoutant une règle pour les fichiers dont on sait avec certitude
247        ## qu'ils sont statiques : les images etc
248        else {
249                ## ne cacher que les css, js, jpg, gif, png, etc.
250                ## le (?\d+) est un éventuel timestamp
251                ## à noter : si apache est bien configuré, cette ligne est inutile
252                if (bereq.url ~ "\.(css|js|jpg|jpeg|gif|png|ico|txt|mp3|ttf)(\?\d+)?$"
253                || bereq.url ~ "^/(local|IMG|extensions|plugins|static)/") {
254                        set beresp.ttl = 600s;
255                        set beresp.http.Cache-Control = "max-age=600";
256                        set beresp.http.Vary = "Accept-Encoding";
257                }
258                ## ne pas cacher une ressource qui ne precise pas d'entete de cache
259                else {
260                        if (
261                        (!beresp.http.Cache-Control && !beresp.http.Expires)
262                        || beresp.http.Cache-Control ~ "no-cache"
263                        || beresp.http.Cache-Control ~ "private" ) {
264                                set beresp.ttl = 0s;
265                        }
266                        #else {
267                        #       set beresp.ttl = 0s;
268                        #       remove beresp.http.Cache-Control;
269                        #}
270                }
271        }
272        ## ne pas conserver une ressource servie vieille aux robots
273        if (beresp.http.X-Varnish-Stale) {
274                set beresp.ttl = 0s;
275                #remove beresp.http.X-Varnish-Stale;
276        }
277
278
279        ## -- INVALIDATIONS --
280        ##
281
282        ## On a vu ci-dessus qu'une page pouvait entrer en cache si elle s'annonçait
283        ## via X-Varnish-Message-OK
284        ##
285        ## Si à l'inverse le backend veut invalider le cache, il suffit
286        ## qu'il envoie un entête X-Varnish-Purge
287        ##
288        if (beresp.http.X-Varnish-Purge) {
289                ban("req.http.host == " + bereq.http.host);
290                unset beresp.http.X-Varnish-Purge;
291                set beresp.ttl = 0s;
292        }
293
294
295        ## -- VAR_MODE --
296        ## Cette section gère les invalidations via le bouton d'admin
297        ## si on demande un var_mode=recalcul on va par principe tout purger
298        ## pour ne pas subir de cache secondaire, par exemple dans local/
299        ## lorsqu'on modifie des CSS ou des images calculées
300        ## En revanche un var_mode=calcul est plus léger
301        if (bereq.url ~ "[?&]var_mode=(recalcul|images)") {
302                ban("req.http.host == " + bereq.http.host);
303                set beresp.ttl = 0s;
304        }
305        ## si on demande un var_mode=calcul on va purger uniquement la page
306        ## demandee, sans son var_mode
307        elsif (bereq.url ~ "[?&]var_mode=calcul") {
308                ban("req.http.host == " + bereq.http.host + " && req.url == " + regsuball(bereq.url,"[&?]var_mode=.*$", ""));
309                set beresp.ttl = 0s;
310        }
311
312        ## -- GRACE --
313        ## Si la réponse est cachable, on peut la conserver pour un maximum d'1h
314        ## au cas où on aurait une panne (maximum des "grace" définies dans RECV)
315        ## http://varnish-cache.org/trac/wiki/VCLExampleGrace
316        set beresp.grace = 1h;
317
318
319        ## -- RANGE --
320        ## seuls les gros fichiers (sons, videos) sont susceptibles de valoir
321        ## un range ; on n'annonce donc le range que pour ceux-la, et on desactive
322        ## l'entete
323        if (beresp.http.Accept-Ranges && beresp.http.content-type !~ "(image|audio|video)/") {
324                unset beresp.http.Accept-Ranges;
325        }
326
327        ## -- ETAG --
328        ## corrige un bug d'apache qui donne le même Etag aux représentations
329        ## gzip et non-gzip d'un même fichier...
330        if (beresp.http.content-encoding == "gzip"
331        && beresp.http.etag) {
332                set beresp.http.Etag = regsub(beresp.http.etag, ".$", "-gzip\0");
333        }
334
335        ## -- ACTION=CRON --
336        ## inutile de solliciter la page action=cron de SPIP plus d'1 fois par 5s
337        ## (à noter : avec les stats en js cette action disparaît)
338        if (bereq.url ~ "\?action=cron$") {
339                set beresp.ttl = 5s;
340        }
341
342}
343
344
345## -- HASH --
346##
347## Cette fonction établit le nom du cache en fonction des caractéristiques
348## de la requête
349sub vcl_hash {
350        hash_data(req.url);
351        if (req.http.host) {
352                hash_data(req.http.host);
353        } else {
354                hash_data(server.ip);
355        }
356        return (lookup);
357}
358
359
360## -- HIT --
361##
362## Cette fonction est appelée quand une page est trouvée dans le cache
363## de Varnish ; on ajoute une logique pour que "force-reload", sur un
364## navigateur correct, n'utilise pas le cache : il conduit jusqu'au backend
365## et met à jour le cache
366sub vcl_hit {
367if (obj.ttl >= 0s) {
368    # normal hit
369    return (deliver);
370  }
371# We have no fresh fish. Lets look at the stale ones.
372  if (std.healthy(req.backend_hint)) {
373
374        # force-refresh will update the cache
375        # http://www.varnish-cache.org/trac/wiki/VCLExampleEnableForceRefresh
376        if (req.http.Cache-Control ~ "no-cache") {
377                # Ignore requests via proxy caches, IE users and badly behaved crawlers
378                # like msnbot that send no-cache with every request.
379                if (! (req.http.Via || req.http.User-Agent ~ "bot|MSIE")) {
380                        #set obj.ttl = 0s;
381                        return (restart);
382                }
383        }
384
385    # Backend is healthy. Limit age to 10s.
386    if (obj.ttl + 10s > 0s) {
387      set req.http.grace = "normal(limited)";
388      return (deliver);
389    } else {
390      # No candidate for grace. Fetch a fresh object.
391      return(fetch);
392   }
393  } else {
394    # backend is sick - use full grace
395    if (obj.ttl + obj.grace > 0s) {
396      set req.http.grace = "full";
397      return (deliver);
398    } else {
399     # no graced object.
400    return (fetch);
401   }
402  }
403}
404
405## -- DELIVER --
406##
407## Cette fonction est appelée à l'envoi final du fichier, sauf (pass)
408##
409## On met le champ Age dans X-Varnish-Age sinon ça fait râler redbot.org
410## On supprime Accept-Ranges de tous les fichiers sauf les images/sons/vidéos
411##
412sub vcl_deliver {
413        {
414                set resp.http.grace = req.http.grace;
415                set resp.http.X-Varnish-Age = resp.http.age;
416                unset resp.http.age;
417                if (resp.http.content-type !~ "(image|audio|video)/") {
418                        unset resp.http.accept-ranges;
419                }
420
421        }
422}
423
424## -- ERROR --
425##
426## Cette fonction est appelée en cas d'erreur, par exemple
427## lorsque l'objet n'est pas en cache et que le backend est
428## en panne (erreur 503).
429##
430## On peut la déclencher volontairement, pour la tester, via l'URL "guru"
431##
432
433sub vcl_synth {
434    set resp.http.Content-Type = "text/html; charset=utf-8";
435    synthetic ({"
436<html><head>
437<meta http-equiv="content-type" content="text/html; charset=utf-8">
438<title>"} + resp.status + {" "} + resp.reason + {"</title>
439<style type="text/css"> 
440body {
441        background: #689ab3;
442        color: #333;
443        margin: 0;
444        font-size:1.3em;
445        font-family: georgia, serif;
446}
447 
448div {
449        margin: 30px 0 0 0;
450        background: #ffffff;
451        opacity: 0.6;
452        width: 650px;
453        padding: 20px 20px 23px 23px;
454}
455 
456p {
457        margin: 0.5em 0 0.5em 0;
458}
459
460p.en {
461        margin: 0 0 0.5em 0;
462}
463
464p.fin {
465        margin: 0.5em 0 0 0;
466}
467 
468p.message {
469        font-size: 1.5em;
470        margin: 0 0 0.5em 0;
471}       
472 
473h1 {
474        font-size: 2em;
475        margin: 0 0 0.5em 0;
476        font-weight:normal;
477        display:none;
478}
479 
480 
481</style>
482</head><body>
483 
484<div> 
485 
486<p class="message">Le service est momentan&#233;ment indisponible.<br /> 
487Veuillez r&#233;essayer un peu plus tard.
488</p>
489
490<p class="en">The service is currently unavailable. Please try again later.
491</p>
492 
493<p class="fin"><small>Erreur "} + resp.status + {" | XID: "} + req.xid + {"</small></p>
494 
495</div>
496
497
498</body></html>
499"});
500    return (deliver);
501}
502
503
504sub vcl_backend_error {
505    set beresp.http.Content-Type = "text/html; charset=utf-8";
506    set beresp.http.Retry-After = "5";
507    synthetic ({"
508<html><head>
509<meta http-equiv="content-type" content="text/html; charset=utf-8">
510<title>"} + beresp.status + {" "} + beresp.reason + {"</title>
511<style type="text/css"> 
512body {
513        background: #689ab3;
514        color: #333;
515        margin: 0;
516        font-size:1.3em;
517        font-family: georgia, serif;
518}
519 
520div {
521        margin: 30px 0 0 0;
522        background: #ffffff;
523        opacity: 0.6;
524        width: 650px;
525        padding: 20px 20px 23px 23px;
526}
527 
528p {
529        margin: 0.5em 0 0.5em 0;
530}
531
532p.en {
533        margin: 0 0 0.5em 0;
534}
535
536p.fin {
537        margin: 0.5em 0 0 0;
538}
539 
540p.message {
541        font-size: 1.5em;
542        margin: 0 0 0.5em 0;
543}       
544 
545h1 {
546        font-size: 2em;
547        margin: 0 0 0.5em 0;
548        font-weight:normal;
549        display:none;
550}
551 
552 
553</style>
554</head><body>
555 
556<div> 
557 
558<p class="message">Le service est momentan&#233;ment indisponible.<br /> 
559Veuillez r&#233;essayer un peu plus tard.
560</p>
561
562<p class="en">The service is currently unavailable. Please try again later.
563</p>
564 
565<p class="fin"><small>Erreur "} + beresp.status + {" | XID: "} + bereq.xid + {"</small></p>
566 
567</div>
568
569
570</body></html>
571"});
572
573    return (deliver);
574}
575 
Note: See TracBrowser for help on using the repository browser.