Déployer cache-control: immutable avec du cache-busting

Déployer cache-control: immutable avec du cache-busting (le 08/02/2018)

L’autre jour, je m’amusais à tester mon site Van11y sur divers outils, et notamment celui-ci : Sonarwhal.

Ce dernier propose une piste d’amélioration que je n’avais pas essayée jusqu’à présent, à savoir implémenter Cache-Control: immutable. J’avais entendu parler de cette possibilité via ma veille habituelle, mais je n’avais jamais essayé en pratique.

cache-control: immutable

Cache-control: immutable

Cette possibilité a été motivée principalement par de gros sites comme Facebook. Ces derniers ont constaté que bon nombre de leurs utilisateurs utilisaient énormément la touche F5 (allez savoir pourquoi !). Les éléments dits statiques sont bien mis en cache, mais toutefois le navigateur doit deviner quels éléments ont été modifiés, il réinterroge donc le serveur qui lui renvoie un code 304, qui lui signifie « Non modifié ».

En pratique, sur de tous petits fichiers, le coût de cette revalidation est plus élevé que de re-télécharger le fichier en question. Ajoutons à cela qu’un site comme Facebook a beaucoup de fichiers statiques (images, etc.) et d’énormes besoins en temps serveur et en bande-passante. Et comme dirait La Palice :

La requête la plus rapide est celle que l’on ne fait jamais.

Bref, l’intérêt de Cache-Control: immutable ? Grosso modo, c’est d’éviter cette étape de validation, en disant au navigateur que l’élément en question ne changera jamais, il est dit « immutable ».

Ce « petit changement » implique certaines précautions.

Avoir une stratégie de cache-busting

J’en avais une sur les CSS et sur le JavaScript (qui sont faits automatiquement), mais j’étais quelque peu fainéant sur d’autres éléments. Qu’à cela ne tienne, c’était l’occasion de s’amuser à le faire comme il faut, sur un site avec relativement peu de contenus.

Autant le dire de suite : dans ce cas, je l’ai fait de manière très artisanale. La plupart des CMS et bon nombre d’autres systèmes le font pour vous automatiquement.

En pratique, sans cette stratégie, si vous envoyez le fichier /images/logo.svg, et si vous deviez mettre à jour ce fichier sur votre serveur, il faudra le renommer. Vous voyez l’écueil ? Changer à la main le nom d’un fichier sur un nombre conséquent de pages n’est absolument pas gérable.

Il existe toutes sortes de techniques de cache-busting, mais la plus passe-partout consiste à utiliser de l’URL-rewriting (la réécriture d’adresse en bon français). En clair, on va demander au serveur de réécrire l’adresse /images/logo_<ici une valeur qu’on fera changer>.svg en /images/logo.svg. Cela peut se faire via un htaccess :

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)_(\d+)\.(css|js)$ $1.$3 [L]

En clair, si l’URL demandée n’est ni un fichier ni un répertoire, et avec les extensions ci-dessus, alors fichier_xxxxxxxx.extension va être réécrit ainsi sur le serveur fichier.extension. Par exemple, css/styles_mini_1516114378.css sera réécrit en css/styles_mini.css (et ce fichier est bien sur mon serveur).

Par contre, c’est bien l’URL css/styles_mini_1516114378.css qui va être mise en cache. Une mise à jour sur la CSS ? Le cache-buster va être mis à jour, par exemple en css/styles_mini_1516114666.css.

En pratique pour le cache-busting

Grosso modo, j’ai repéré tous les types de fichiers qui devaient en bénéficier, et j’ai mis cela dans mon htaccess principal :

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)_(\d+)\.(css|js|webmanifest|ico|png|jpg|svg|js|eot|ttf|woff|woff2)$ $1.$3 [L]

Ensuite, il a fallu ajouter le cache-buster partout où il est nécessaire. Sur les img de contenu, j’ai utilisé une fonction en PHP qui renvoie le timestamp Unix de dernière modification du fichier (filemtime()) et qui insère le cache-buster dans le nom du fichier.

return '_'.filemtime($prefix. $chemin_img);

Dans la CSS, pour les rares images et autres éléments utilisés… fainéantise absolue, je l’ai fait à la main. Si si. Il existe des outils qui le font pour vous. Là, les besoins étaient si maigres… que j’ai eu la flemme.

Pour les fichiers CSS, là j’avais déjà des fonctions qui le faisaient automatiquement, en plus de faire d’autres choses (minification, calcul d’intégrité, etc.). Idem pour le JavaScript.

Comme les démos du site sont reprises depuis les dépôts Github de chaque projet (pour m’éviter de me fatiguer quand je les mets à jour), j’ai modifié les fonctions qui récupèrent les fichiers pour ajouter les cache-busters aux bons endroits (CSS et JavaScript). Hop, tout est automatisé, point d’effort.

En pratique pour déployer l’en-tête proprement dite

Une fois la stratégie de cache-busting correctement déployée, il ne reste plus grand chose à faire, j’ai mis ceci dans mon htaccess habituel :

<FilesMatch "\.(css|js|webmanifest|ico|png|jpg|svg|js|eot|ttf|woff|woff2)$">
<IfModule mod_headers.c>
Header set Cache-Control "public, max-age=31536000, immutable"
...
</IfModule>
</FilesMatch>

Et c’est terminé.

Un point de détail amusant : Sonarwhal va – à juste titre – vous gronder si vous déployez Cache-Control: immutable sans stratégie de cache-busting. L’outil ne reconnaissait pas celle que j’avais utilisée, je le signale aux développeurs, qui ont été super réactifs et qui ont mis l’outil à jour. Un bravo à eux !

Un autre point de détail : Cache-Control: immutable ne sera honoré sous Firefox que si le contenu est servi en HTTPS. Cela semble assez logique : il faut être sûr que le contenu n’ait pas été modifié entre le serveur et le navigateur.

En conclusion

Disons-le clairement, l’impact sur les performances sur un site ultra-léger et rapide comme Van11y est quasi-nul. C’est d’autant plus beau que c’est totalement dispensable. :)

C’est un petit exercice de style auquel je me suis livré, plus pour m’amuser qu’autre chose. C’est toujours l’occasion de mettre des bonnes pratiques en place, sur des projets sympathiques et peu chronophages. Ces bonnes pratiques sont utiles même si vous ne déployez pas Cache-Control: immutable.

Pour en savoir plus :

Permalien :

Flux RSS des commentaires de ce billet : https://www.nicolas-hoffmann.net/rss/commentaires.php?id_news=1710

1 commentaire

Posté par Cédric le 09/02/2018 à 18:07:58
Merci, j'ai des sites nettement moins légers à améliorer (clin d’oeil)

Ajouter un commentaire









L'option « Se souvenir de mes informations » utilise un cookie, elle ne sera pas effective si vous les avez désactivés.

Les balises HTML ne seront pas interprétées, il est donc inutile d'en mettre. Par contre, les sauts de lignes de votre commentaire seront pris en compte, ne mettez donc pas de <br />, le site s'en chargera. Bien sûr, un commentaire vide ne sera pas ajouté !

L'auteur (autrement dit moi) n'est pas responsable des éventuelles fautes d'orthographe dans les commentaires.
Tout propos raciste et/ou insultant sera supprimé sans préavis. Les commentaires hors de propos destinés à faire de la pub pour des sites seront également supprimés sans ménagement.

Je vous prie de me pardonner, j'ai énormément de mal à lire le "langage" SMS, il n'est donc pas du tout interdit de s'abstenir de l'utiliser. Qui plus est, vous avez sûrement un clavier digne de ce nom et pas celui d'un téléphone portable. Ne vous gênez pas pour utiliser l'option "Prévisualiser" si vous voulez vous relire avant de poster, je vous en remercie d'avance !

Cet article a été écrit par Nicolas Hoffmann.

Ce site est la propriété de Nicolas Hoffmann.
Tous droits réservés, les textes du blog sont publiés sous licence CC BY-NC-SA.