Serveur dédié : installer et configurer Varnish 4

Cette semaine, j’ai décidé de mettre mon installation de Varnish à jour.

La version 3.0.5 date de décembre 2013 et il est temps de mettre le serveur à jour pour bénéficier des dernières nouveautés et corrections de bugs. Nous passons donc de Varnish 3 à Varnish 4.

Cela ne se fait pas sans peine car chez Varnish, ils renomment certaines directives d’une version à l’autre… ce qui fait planter le serveur Varnish puisqu’il ne reconnait plus les directives.

Résultat : le fichier de configuration de la version précédente plantera obligatoirement sous la dernière version !

Ce tutoriel en 3 étapes nous donnera l’occasion de mettre à jour Varnish et de scinder notre fichier de configuration en plusieurs modules de manière à en simplifier l’édition et la maintenance futures.

Etape 1 : mise à jour des dépôts Varnish

Pour mettre à jour Varnish, il suffit de pointer apt vers les derniers dépôts à jour. On édite donc /etc/apt/sources.list :

nano /etc/apt/sources.listCode language: PHP (php)

et on y met à jour nos dépôts:

# varnish
deb http://repo.varnish-cache.org/debian/ wheezy varnish-4.0Code language: PHP (php)

On rafraîchit la liste des paquets et on lance la mise à jour :

apt-get update && apt-get upgradeCode language: JavaScript (javascript)

Varnish est maintenant mis à jour mais loin d’être fonctionnel étant donné que le format du fichier de configuration a changé.

Etape 2 : le nouveau fichier de configuration de Varnish 4 pour WordPress

Certaines directives ont changé de nom et, malgré avoir lu le guide de migration officiel, j’ai modifié mon fichier de configuration en corrigeant les erreurs une à une. Cela prend du temps mais au final, le fichier est plus clair qu’avant.

Voici donc mon nouveau fichier default.vcl optimisé pour WordPress :

# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;

# Default backend definition. Set this to point to your content server.
backend default {
	.host = "127.0.0.1";
	.port = "8080";
	.connect_timeout = 600s;
	.first_byte_timeout = 600s;
	.between_bytes_timeout = 600s;
	.max_connections = 800;
}

backend example {
.host = "127.0.0.1";
.port = "8088";
}

import std;
include "xforward.vcl";
include "bigfiles.vcl";
include "mobile_cache.vcl";
include "mobile_pass.vcl";
include "static.vcl";

sub vcl_recv {
    # Happens before we check if we have this in cache already.
    # Typically you clean up the request here, removing cookies you don't need,
    # rewriting the request, etc.
    
    if (req.http.host ~ "example.com") {
        set req.backend_hint = example;
    } elseif (req.http.host ~ "skyminds.net") {
        set req.backend_hint = default;
    }
                 
        # ---------------------- WORDPRESS SPECIFIC CONFIG -------------------- #
	# Did not cache the RSS feed
	if (req.url ~ "/feed") {
	return (pass);
	}
	# Blitz hack
	if (req.url ~ "/mu-.*") {
	return (pass);
	}
	# Did not cache the admin and login pages
	if (req.url ~ "/wp-(login|admin)") {
	return (pass);
	}
	
	# First remove the Google Analytics added parameters, useless for our backend
	if(req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=") {
		set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
		set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
		set req.url = regsub(req.url, "\?&", "?");
		set req.url = regsub(req.url, "\?$", "");
	}
	# Remove the "has_js" cookie
	set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");
	# Remove any Google Analytics based cookies
	set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
	# Remove the Quant Capital cookies (added by some plugin, all __qca)
	set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");
	# Remove the wp-settings-1 cookie
	set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", "");
	# Remove the wp-settings-time-1 cookie
	set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", "");
	# Remove the wp test cookie
	set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", "");
	# remove cookies for comments cookie to make caching better.
	set req.http.cookie = regsub(req.http.cookie, "dcd9527364a17bb2ae97db0ead3110ed=[^;]+(; )?", "");
	
	# remove ?ver=xxxxx strings from urls so css and js files are cached.
	set req.url = regsub(req.url, "\?ver=.*$", "");
	# Remove "replytocom" from requests to make caching better.
	set req.url = regsub(req.url, "\?replytocom=.*$", "");
	# Strip hash, server doesn't need it.
	set req.url = regsub(req.url, "\#.*$", "");
	# Strip trailing ?
	set req.url = regsub(req.url, "\?$", "");

	# Are there cookies left with only spaces or that are empty?
	if (req.http.cookie ~ "^ *$") {
	unset req.http.cookie;
	}	
	# Drop any cookies sent to WordPress.
	if (!(req.url ~ "wp-(login|admin)")) {
                       unset req.http.cookie;
        }
        # Cache the following files extensions
	if (req.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico)") {
	unset req.http.cookie;
	}
	
	# Normalize Accept-Encoding header and compression
	# https://varnish-cache.org/docs/3.0/tutorial/vary.html
	if (req.http.Accept-Encoding) {
	# Do no compress compressed files...
	if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
	unset req.http.Accept-Encoding;
	} elsif (req.http.Accept-Encoding ~ "gzip") {
	set req.http.Accept-Encoding = "gzip";
	} elsif (req.http.Accept-Encoding ~ "deflate") {
	set req.http.Accept-Encoding = "deflate";
	} else {
	unset req.http.Accept-Encoding;
	}
	}
	
	# Check the cookies for wordpress-specific items
	if (req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_") {
	return (pass);
	}
	if (!req.http.cookie) {
	unset req.http.cookie;
	}
	# ---------------------- /WORDPRESS SPECIFIC CONFIG -------------------- #
	
	# No cache for big video files
	if (req.url ~ "\.(avi|mp4)") {
		return (pass);
	}   
	
	# Do not cache HTTP authentication and HTTP Cookie
	if (req.http.Authorization || req.http.Cookie) {
	# Not cacheable by default
	return (pass);
	}
	# Cache all others requests
	return (hash);                     
}

sub vcl_backend_response {
    # Happens after we have read the response headers from the backend.
    # 
    # Here you clean the response headers, removing silly Set-Cookie headers
    # and other mistakes your backend does.
    
    # Drop any cookies WordPress tries to send back to the client.
    if (!(bereq.url ~ "wp-(login|admin)")) {
                       unset beresp.http.set-cookie;
            }
            
}

sub vcl_deliver {
    # Happens when we have all the pieces we need, and are about to send the
    # response to the client.
    # 
    # You can do accounting or modifying the final object here.
    
	if (obj.hits > 0) {
	set resp.http.X-Cache = "cached";
	} else {
	set resp.http.x-Cache = "uncached";
	}

	# Remove some headers
	unset resp.http.X-Powered-By;
	unset resp.http.X-Varnish;
	unset resp.http.Via;
	unset resp.http.Age;
	unset resp.http.Link;
	unset resp.http.Server;
	return (deliver);
}

sub vcl_pipe {
	# Note that only the first request to the backend will have
	# X-Forwarded-For set. If you use X-Forwarded-For and want to
	# have it set for all requests, make sure to have:
	# set bereq.http.connection = "close";
	# here. It is not set by default as it might break some broken web
	# applications, like IIS with NTLM authentication.
	#set bereq.http.Connection = "Close";
	return (pipe);
}

# The data on which the hashing will take place
sub vcl_hash {
	hash_data(req.url);
	if (req.http.host) {
		hash_data(req.http.host);
	} else {
		hash_data(server.ip);
	}
	# hash cookies for requests that have them
	if (req.http.Cookie) {
		hash_data(req.http.Cookie);
	}
}
sub vcl_hit {
	return (deliver);
}
sub vcl_miss {
	return (fetch);
}
sub vcl_init {
	return (ok);
}
sub vcl_fini {
	return (ok);
}Code language: PHP (php)

Etape 3 : fichiers de configuration supplémentaires

Avec cette nouvelle version, je me suis servi des fichiers de configuration de DreamHost disponibles sur Github.

Le fait de séparer certaines actions est plus simple à gérer : les objets statiques seront gérés par le fichier static.vcl, les gros fichiers par bigfiles.vcl et toute la configuration pour WordPress sera dans le fichier de configuration principal. C’est plus simple à maintenir.

Il m’a fallu modifier certains de ces fichiers donc je les redonne ici.

1. xforward.vcl permet d’ajouter l’entête X-Forwarded-For et normalise l’IP du client

On édite :

nano /etc/varnish/xforward.vcl

avec :

# xforward.vcl -- X-Forwarded-For HTTP Headers
#
# This should generally be loaded first to make sure that the headers
# get set appropriately for all requests.  Note that when using this
# you MUST NOT fall through to the VCL default handler for vcl_recv
# since that will run the code again, resulting in the client.ip
# being added twice.
sub vcl_recv {
	if (req.restarts == 0) {
		if (req.http.X-Forwarded-For) {
			set req.http.X-Forwarded-For =
				req.http.X-Forwarded-For + ", " + client.ip;
		} else {
			set req.http.X-Forwarded-For = client.ip;
		}
	}
}Code language: PHP (php)

2. bigfiles.vcl s’occupe de transmettre les gros fichiers sans passer par le cache

On édite :

nano /etc/varnish/bigfiles.vcl

avec :

# bigfiles.vcl -- Bypass Cache for Large Files
#
# Copyright (C) 2013 DreamHost (New Dream Network, LLC)
#
# You must have "import std;" in your main vcl:
# import std;

sub vcl_backend_response {
# Bypass cache for files > 10 MB
if (std.integer(beresp.http.Content-Length, 0) > 10485760) {
	set beresp.uncacheable = true;
        set beresp.ttl = 120s;
        return (deliver);
}
}Code language: PHP (php)

3. mobile_cache.vcl crée un cache à part pour les clients mobiles

On édite :

nano /etc/varnish/mobile_cache.vcl

avec :

# mobile_cache.vcl -- Separate cache for mobile clients
#
# Copyright (C) 2013 DreamHost (New Dream Network, LLC)
#
# If the User-Agent looks like a mobile device, then we add the string
# "mobile" to the hash_data.  This results in mobile devices having
# a separate cache from non-mobile devices.
#
# Note that if the backend does anything more sophisticated than having
# a "desktop" and a "mobile" version of pages (for example serving one
# page to iPhones and another to Androids), this will not be desirable.
# Also if the backend disagrees with this logic as far as what is a
# "mobile" User-Agent, then we may save the wrong version of pages in
# the cache.
sub vcl_hash {
	# General User-Agent list (anything that remotely looks like a mobile device)
	if (req.http.User-Agent ~ "(?i)ipod|android|blackberry|phone|mobile|kindle|silk|fennec|tablet|webos|palm|windows ce|nokia|philips|samsung|sanyo|sony|panasonic|ericsson|alcatel|series60|series40|opera mini|opera mobi|au-mic|audiovox|avantgo|blazer|danger|docomo|epoc|ericy|i-mode|ipaq|midp-|mot-|netfront|nitro|pocket|portalmmm|rover|sie-|symbian|cldc-|j2me|up\.browser|up\.link|vodafone|wap1\.|wap2\.") {
		hash_data("mobile");
	}
}Code language: PHP (php)

4. mobile_pass laisse passer les requêtes initiées par des mobiles ou tablettes

On édite :

nano /etc/varnish/mobile_pass.vcl

avec :

# mobile_pass.vcl -- Mobile pass-through support for Varnish
#
# Copyright (C) 2013 DreamHost (New Dream Network, LLC)
#
# This simply bypasses the cache for anything that looks like a mobile
# (or tablet) device.
# Also passes through some requests that are specifically for the WordPress
# Jetpack mobile plugin.
sub vcl_recv {
	# Rules specifically for the Jetpack Mobile module
	if (req.url ~ "\?(.*&)?(ak_action|app-download)=") {
		return(pass);
	}
	if (req.http.Cookie ~ "(^|;\s*)akm_mobile=") {
		return(pass);
	}

	# General User-Agent blacklist (anything that remotely looks like a mobile device)
	if (req.http.User-Agent ~ "(?i)ipod|android|blackberry|phone|mobile|kindle|silk|fennec|tablet|webos|palm|windows ce|nokia|philips|samsung|sanyo|sony|panasonic|ericsson|alcatel|series60|series40|opera mini|opera mobi|au-mic|audiovox|avantgo|blazer|danger|docomo|epoc|ericy|i-mode|ipaq|midp-|mot-|netfront|nitro|pocket|portalmmm|rover|sie-|symbian|cldc-|j2me|up\.browser|up\.link|vodafone|wap1\.|wap2\.") {
		return(pass);
	}
}Code language: PHP (php)

5. static.vcl s’occupe de la mise en cache des objets statiques

On édite :

nano /etc/varnish/static.vclCode language: JavaScript (javascript)

avec :

# static.vcl -- Static File Caching for Varnish
#
sub vcl_recv {
	if (req.method ~ "^(GET|HEAD)$" && req.url ~ "\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)(\?.*)?$") {
		if (req.url ~ "nocache") {
			return(pass);
		}
		set req.url = regsub(req.url, "\?.*$", "");
		unset req.http.Cookie;
		return(hash);
	}
	
		# Added security, the "w00tw00t" attacks are pretty annoying so lets block it before it reaches our webserver
	if (req.url ~ "^/w00tw00t"){
	     return( synth(403, "not permitted !"));	
	}
}

sub vcl_backend_response {
	if (bereq.method ~ "^(GET|HEAD)$" && bereq.url ~ "\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$") {
		unset beresp.http.set-cookie;
		set beresp.ttl = 24h;
		set beresp.grace = 5m;
	}
	
	###	
	if (beresp.http.content-type ~ "text") {
              set beresp.do_gzip = true;
        }

        # set minimum timeouts to auto-discard stored objects
        set beresp.grace = 5m;
	# no cache for error pages
	if ( beresp.status == 404 || beresp.status >= 500 ) {
		set beresp.ttl = 0s;
		return(deliver);
	}
        # Set 2min cache if unset for static files
	if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") {
	        set beresp.ttl = 120s;
	        return (deliver);
	}
	# If WordPress cookies found then page is not cacheable
	if (bereq.http.Cookie ~"(wp-postpass|wordpress_logged_in|comment_author_)") {
		set beresp.ttl = 0s;
		return(deliver);
	}
	# don't cache search results
	if( bereq.url ~ "\?s=" ){
		 return(deliver);
	}

	return (deliver);
}Code language: PHP (php)

A titre de référence, je me suis également inspiré de :

  • https://github.com/dreamhost/varnish-vcl-collection/tree/master/lib
  • https://github.com/mattiasgeniar/varnish-4.0-configuration-templates/blob/master/default.vcl
  • https://github.com/nicolargo/varnish-nginx-wordpress/blob/master/varnish/varnish4-wordpress

Conclusion

Voilà, vous venez de mettre Varnish à jour et l’organisation de la configuration en plusieurs fichiers rend la lecture plus lisible.

Synopsis » Monter un serveur dédié de A à Z

  1. Serveur dédié : installation d’Apache, PHP, MySQL et Webmin
  2. Serveur dédié : créer la base de données MySQL et importer WordPress
  3. Serveur dédié : créer et activer un Virtual Host sous Apache
  4. Serveur dédié : changer les DNS du nom de domaine et le faire pointer vers le serveur
  5. Serveur dédié : sécurisation des services avec iptables et fail2ban
  6. Serveur dédié : sécurisation de la couche TCP/IP
  7. Serveur dédié : création d’un serveur mail Postfix (sécurisé avec Saslauthd et certificat SSL) et Courier (accès POP et IMAP) utilisant une base MySQL d’utilisateurs/domaines virtuels
  8. Serveur dédié : sécuriser Apache 2 avec ModSecurity
  9. Serveur dédié : CHMOD récursif sur des fichiers ou répertoires en ligne de commande
  10. Serveur dédié : installer APC comme système de cache et configurer Varnish comme reverse-proxy pour Apache pour améliorer les performances
  11. Serveur dédié : afficher la véritable IP derrière un reverse-proxy comme Varnish
  12. Serveur dédié : intégrer SSH à WordPress pour mettre à jour le core, les plugins et les thèmes
  13. Serveur dédié : installer la dernière version d’APC par SVN
  14. Serveur dédié : analyse des performances du serveur
  15. Serveur dédié : mettre à jour le noyau Debian de la Kimsufi
  16. Serveur dédié : sauvegarde automatique des fichiers avec Backup Manager sur le serveur de sauvegarde OVH
  17. Serveur dédié : configurer la limite mémoire pour PHP et Suhosin
  18. Bash : supprimer tous les fichiers et sous-répertoires d’un répertoire
  19. Serveur dédié : impossible de se connecter à un port distant
  20. Rsync: rapatrier les fichiers du serveur à la maison
  21. Bash : réparer les tables MySQL en cas de crash
  22. Serveur dédié : création d’une seedbox avec Transmission
  23. Serveur dédié : des paquets LAMP à jour sous Debian
  24. Serveur dédié : mise à jour vers Debian 7 Wheezy
  25. Serveur dédié : activer X11 forwarding pour SSH
  26. Serveur dédié : optimiser toutes les images JPG et PNG avec OptiPNG et JpegOptim
  27. Postfix : résoudre l’erreur “fatal: www-data(33): message file too big”
  28. Serveur dédié : mise en place de l’IPv6
  29. WordPress : accorder les bonnes permissions aux fichiers et dossiers avec chown et chmod
  30. WordPress : héberger les images sur un sous-domaine
  31. Serveur dédié : ajouter l’authentification SPF, Sender-ID et DKIM à Postfix et Bind9 avec opendkim
  32. Apache : lorsque le domaine seul (sans WWW) renvoie une erreur 403
  33. Serveur dédié : sécuriser Apache avec HTTPS (HTTP avec la couche TLS/SSL) en Perfect Forward Secrecy
  34. Serveur dédié : passer WordPress en HTTPS (TLS/SSL)
  35. Serveur dédié : configurer Webmin en TLS avec un certificat SSL
  36. Serveur dédié : configurer Transmission pour accéder au WebUI via TLS-SSL
  37. Serveur dédié : installer et configurer Varnish 4
  38. Serveur dédié : passage au mod FastCGI et PHP-FPM avec Apache MPM Worker
  39. Récupérer un serveur Kimsufi après un plantage de kernel avec le mode rescue OVH
  40. Serveur dédié : configurer Postfix et Courier pour utiliser TLS-SSL en Perfect Forward Secrecy
  41. Serveur dédié : retirer Varnish, devenu inutile avec HTTPS
  42. Serveur dédié : installer la dernière version d’OpenSSL sous Debian
  43. Serveur dédié : activer l’IP canonique du serveur sous Apache
  44. Serveur dédié : mise à jour vers PHP 5.6
  45. MySQL : convertir les tables MyISAM au format InnoDB
  46. Serveur dédié : optimiser toutes les images GIF avec GIFsicle
  47. Serveur dédié : migration de MySQL vers MariaDB
  48. BASH : lister, bloquer et débloquer des adresses IP avec iptables
  49. Serveur dédié : produire une meilleure réserve d’entropie avec haveged
  50. Serveur dédié : mettre en place DNSSEC pour sécuriser les DNS du domaine
  51. Serveur dédié : mise en place du protocole DANE
  52. 8 règles d’or pour bien déployer DNSSEC et DANE
  53. Serveur dédié : installer PHP7 FPM avec FastCGI sous Debian
  54. Serveur dédié : optimiser la couche TCP
  55. Fail2Ban: protéger Postfix contre les attaques DoS de types AUTH, UNKNOWN et EHLO
  56. Serveur dédié : mettre à jour Apache pour HTTP/2
  57. Serveur dédié : ajouter le domaine à la liste HSTS preload
  58. Serveur dédié : ajouter l’authentification DMARC à Postfix et BIND
  59. Serveur dédié : à la recherche de l’inode perdue ou comment résoudre le problème “no space left on device”
  60. Serveur dédié : installer NginX avec support HTTP2 et certificat SSL, PHP, MariaDB sous Debian

Rencontrez-vous des défis avec votre site WordPress ou WooCommerce? Laissez-moi les résoudre pour vous.

Discutons des solutions possibles »

Articles conseillés :

Matt

Matt Biscay est développeur WordPress et WooCommerce certifié chez Codeable, ainsi que sysadmin qualifié et enseignant-chercheur. Passionné par le code performant et les solutions sécurisées, je m'efforce d'offrir une expérience utilisateur exceptionnelle sur chaque projet.

Vous avez aimé cet article ? Vous avez un projet en tête et vous pensez que je pourrais vous aider à le concrétiser ? N'hésitez pas à me contacter, je serais ravi de discuter avec vous de votre projet !

6 pensées sur “Serveur dédié : installer et configurer Varnish 4”

  1. article tres sympa.
    J’etait en version 3, tout marchait nickel. Me suis dit, on va suivre le tuto est passer en version 4…et là, c est le drame…

    Marche plus du tout

    Reply
  2. Fonctionne très bien, j’y ai même rajouté deux/trois petites choses :)

    ma config :

    Client -> Nginx(frontend SSL Termination) -> Varnish(Cache) -> Nginx(backend)

    Fonctionne du tonnerre ^^

    Reply

Opinions