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

Vous avez un projet WordPress ou WooCommerce en tête? Transformez votre vision en réalité avec mon expertise reconnue.

Parlons de votre projet dès aujourd'hui »

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, n'hésitez pas à me contacter, je serai ravi d'en discuter avec vous !

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