Compression Brotli, Gzip et Zstandard : ce que pèse vraiment le texte de vos pages

Comment fonctionne la compression HTTP, en quoi Gzip, Brotli et Zstandard diffèrent, quand compresser à l’avance et quand à la volée, comment configurer le serveur et quel effet attendre sur les Core Web Vitals.

Un document texte de 450 Ko transformé par une flèche en document compressé d’environ 100 Ko, à côté des formats gzip, br et zstd

De toutes les optimisations possibles pour un site web, la compression des ressources texte est probablement celle qui offre le meilleur rapport effort/résultat. HTML, CSS, JavaScript, JSON et SVG sont du texte, et le texte se compresse très bien : un fichier JavaScript de 450 kilooctets peut voyager sur le réseau en pesant à peine plus de 100. Le navigateur le reçoit compressé, le décompresse en quelques millisecondes et l’utilise comme s’il était arrivé entier. Moins de données à transférer, ce sont des pages qui arrivent plus vite, surtout sur les réseaux mobiles.

Pendant des années, la référence a été Gzip. Puis est arrivé Brotli, désormais pris en charge partout, et plus récemment Zstandard, un algorithme né pour compresser les données dans les systèmes de Facebook et désormais pris en charge par les principaux navigateurs basés sur Chromium et par Firefox. Dans cet article, nous voyons comment fonctionne la compression HTTP, en quoi les trois formats diffèrent, comment choisir les niveaux et les modes, comment les configurer et quel effet attendre sur les Core Web Vitals.

Comment fonctionne la compression HTTP

La compression HTTP repose sur une négociation simple. À chaque requête, le navigateur indique, avec l’en-tête Accept-Encoding, quels formats il sait décompresser : par exemple gzip, deflate, br, zstd. Le serveur choisit l’un des formats acceptés, compresse la réponse et le déclare dans l’en-tête Content-Encoding. Le navigateur lit l’en-tête, décompresse et utilise le contenu. Si le serveur ne compresse pas, ou si le navigateur n’accepte aucun format, la ressource voyage telle quelle.

Schéma de la négociation de la compression : le navigateur envoie Accept-Encoding avec gzip, deflate, br et zstd ; le serveur ou le CDN dispose des versions zst, br, gz et non compressée du fichier, choisit la meilleure parmi celles acceptées et répond avec Content-Encoding zstd et Vary Accept-Encoding ; le navigateur décompresse et utilise la version choisie
Le navigateur déclare ce qu’il sait décompresser, le serveur choisit et le déclare dans la réponse.

Un détail important concerne les caches intermédiaires, comme les CDN et les proxys. Comme la même URL peut être servie dans des formats différents selon le navigateur, la réponse doit inclure Vary: Accept-Encoding, qui indique aux caches de conserver des versions distinctes pour chaque format. C’est le même mécanisme que celui vu pour la négociation des formats d’image : sans lui, un cache pourrait servir une réponse compressée en Zstandard à un navigateur incapable de la décompresser.

Gzip : le standard universel

Gzip, basé sur l’algorithme Deflate, est pris en charge par tous les navigateurs et tous les serveurs depuis plus de vingt ans. Il propose des niveaux de compression de 1 à 9 : les niveaux bas sont très rapides et compressent moins, les niveaux élevés compressent davantage mais demandent plus de temps de processeur. Le niveau 6, par défaut dans de nombreuses configurations, est un bon compromis. Sur des fichiers texte typiques, Gzip réduit la taille de 65 à 75 %.

Aujourd’hui, Gzip est surtout le format de repli : celui à proposer aux clients qui ne prennent en charge rien de mieux, comme certains outils automatisés, d’anciennes bibliothèques HTTP et quelques bots. Un serveur moderne doit toujours le prendre en charge, mais le servir aux navigateurs actuels revient à renoncer à quelques points de pourcentage de compression.

Brotli : plus de compression pour le web

Brotli a été développé par Google et conçu spécifiquement pour le web. Outre un algorithme plus efficace, il intègre un dictionnaire de mots et de fragments courants en HTML, CSS et JavaScript, qui lui permet de mieux compresser, surtout les fichiers de petite et moyenne taille. Il est pris en charge par tous les navigateurs modernes, mais uniquement sur les connexions HTTPS, et propose des niveaux de 0 à 11.

À vitesse égale, Brotli compresse en général 15 à 20 % mieux que Gzip. Aux niveaux les plus hauts, 10 et 11, le gain augmente encore, mais la compression devient très lente : des dizaines ou des centaines de fois plus lente que les niveaux intermédiaires. On aboutit ainsi à la distinction la plus importante dans la configuration de la compression, celle entre compression statique et dynamique, sur laquelle nous reviendrons bientôt. La décompression, en revanche, est rapide à n’importe quel niveau : pour le navigateur, un fichier compressé au niveau 11 ne coûte pas plus qu’un fichier compressé au niveau 5.

Zstandard : le nouveau venu

Zstandard, abrégé en zstd, a été créé par Yann Collet chez Facebook et publié en 2016. Il est vite devenu un standard dans de nombreux domaines hors du web : systèmes de stockage, bases de données, paquets de distributions Linux, systèmes de fichiers, transferts de données entre serveurs. Il est décrit dans la RFC 8878, qui l’enregistre aussi comme codage de contenu HTTP.

Sa caractéristique distinctive est la vitesse. Aux niveaux bas et intermédiaires, Zstandard compresse beaucoup plus vite que Gzip tout en obtenant des taux de compression similaires ou meilleurs, et il décompresse encore plus vite. Aux niveaux élevés, jusqu’à 19 et au-delà avec les modes extrêmes, il se rapproche de Brotli, sans généralement le dépasser sur les fichiers web typiques, où le dictionnaire intégré de Brotli fait la différence. En bref : Brotli reste le champion du taux de compression sur les ressources web statiques, tandis que Zstandard offre le meilleur équilibre entre compression et vitesse quand on compresse à la volée.

Comparaison illustrative de la taille d’un fichier JavaScript de 450 kilooctets : gzip niveau 6, 128 kilooctets, moins 72 pour cent, rapide ; zstd niveau 3, 118 kilooctets, moins 74 pour cent, très rapide ; brotli niveau 5, 112 kilooctets, moins 75 pour cent, rapide ; zstd niveau 19, 104 kilooctets, moins 77 pour cent, lent ; brotli niveau 11, 98 kilooctets, moins 78 pour cent, très lent
Les taux réels dépendent du fichier, mais l’ordre est typique : Brotli compresse le plus, Zstandard est le plus rapide.

Côté prise en charge, Chrome et les autres navigateurs basés sur Chromium acceptent Zstandard depuis le printemps 2024, et Firefox a suivi peu après. Safari, au moment de la rédaction, ne l’annonce pas dans l’en-tête Accept-Encoding. Grâce à la négociation, ce n’est pas un problème : les navigateurs qui le prennent en charge reçoivent du Zstandard, les autres du Brotli ou du Gzip. Côté serveur, la prise en charge progresse : certains CDN, comme Cloudflare, le proposent déjà aux navigateurs ; des serveurs web comme Caddy l’intègrent nativement ; pour nginx et Apache, il existe des modules tiers. Il est raisonnable de s’attendre à ce qu’il devienne de plus en plus courant dans les années à venir.

Compression statique et dynamique

Choisir le format ne règle que la moitié de la question. L’autre moitié est de savoir quand compresser, et la réponse change selon le type de ressource.

Les ressources statiques, comme les feuilles de style, les scripts, les polices SVG et les fichiers JSON qui ne changent pas d’une requête à l’autre, peuvent être compressées une seule fois, à l’avance, au niveau maximal. Le coût de la compression, même élevé, est payé une fois au moment de la publication, et chaque requête suivante reçoit le plus petit fichier possible. C’est la compression statique, ou précompression : à côté de app.js, on génère app.js.br, app.js.zst et app.js.gz, et le serveur choisit le bon fichier selon l’en-tête du navigateur.

# Précompression au moment de la publication
for f in dist/*.{js,css,svg,json}; do
	brotli --best --keep "$f"          # app.js.br, niveau 11
	zstd -19 --quiet --keep "$f"       # app.js.zst, niveau 19
	gzip -9 --keep "$f"                # app.js.gz, niveau 9
done

Les ressources dynamiques, comme le HTML que WordPress génère à chaque requête ou les réponses d’API, doivent en revanche être compressées à la volée, pendant que l’utilisateur attend. Ici, le temps de compression s’ajoute au TTFB, et un niveau trop élevé peut annuler l’avantage d’un fichier plus petit. Pour la compression dynamique, on utilise des niveaux intermédiaires : Brotli entre 4 et 6, Gzip autour de 6, Zstandard entre 3 et 6. C’est là que Zstandard montre sa force : à temps de processeur égal, il compresse plus que Gzip et souvent autant que Brotli aux niveaux intermédiaires.

Les pages servies depuis un cache de page sont un cas intermédiaire : le HTML est dynamique, mais il est généré une fois et servi de nombreuses fois. De nombreuses solutions de cache et de nombreux CDN stockent la page déjà compressée, de sorte que le coût de compression n’est payé qu’à la génération. Dans ces cas, on peut utiliser un niveau plus élevé que pour une compression à chaque requête.

Configuration du serveur

La configuration dépend du logiciel. Un exemple pour nginx avec le module Brotli, qui est le cas le plus courant, combine compression statique et dynamique :

# Compression dynamique
gzip on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_vary on;
gzip_types text/css application/javascript application/json image/svg+xml;

brotli on;
brotli_comp_level 5;
brotli_types text/css application/javascript application/json image/svg+xml;

# Compression statique : utiliser les fichiers .br et .gz déjà présents
brotli_static on;
gzip_static on;

Avec Caddy, la configuration est encore plus simple, car il prend en charge nativement Zstandard et Gzip et choisit le format selon les préférences du navigateur :

example.com {
	encode zstd gzip
	file_server {
		precompressed br zstd gzip
	}
}

Sur un hébergement géré ou derrière un CDN, il n’y a souvent rien à configurer : la compression est appliquée automatiquement, et le CDN peut compresser en Brotli ou en Zstandard même quand le serveur d’origine n’utilise que Gzip. Il vaut quand même la peine de vérifier ce qui arrive réellement au navigateur, car les réglages par défaut ne sont pas toujours optimaux, comme nous l’avons vu à propos de Cloudflare.

Ce qu’il faut compresser et ce qu’il ne faut pas

La compression HTTP doit s’appliquer aux ressources texte : HTML, CSS, JavaScript, JSON, XML, SVG, fichiers texte et polices dans des formats non compressés, comme TTF. Elle ne doit pas s’appliquer aux formats déjà compressés en interne : images JPEG, PNG, WebP et AVIF, polices WOFF2, vidéo, audio, archives ZIP et PDF. Les compresser à nouveau ne réduit presque rien et gaspille du processeur, côté serveur comme côté appareil. Les fichiers très petits, de moins d’un kilooctet, n’en tirent pas non plus profit : l’en-tête de compression peut peser plus que le gain.

Un cas particulier concerne la sécurité. Compresser des réponses qui contiennent à la fois des données secrètes, comme les jetons de protection des formulaires, et un contenu contrôlable par un attaquant, comme des paramètres de requête reflétés dans la page, peut vous exposer à une famille d’attaques connue sous le nom de BREACH, qui déduit le secret en observant la taille des réponses compressées. Les contre-mesures ne consistent pas à renoncer à la compression, mais à ne pas refléter les saisies de l’utilisateur près des secrets et à faire varier les jetons à chaque requête, comme le font les frameworks modernes.

L’effet sur les Core Web Vitals

La compression agit sur le temps de transfert des ressources, et donc sur toutes les métriques qui dépendent de leur arrivée. Le HTML compressé arrive plus tôt et le navigateur peut commencer plus tôt à construire la page ; le CSS bloquant le rendu arrive plus tôt et le premier affichage a lieu plus tôt ; les scripts nécessaires arrivent plus tôt. L’effet est le plus visible sur le LCP et le First Contentful Paint, et il croît avec la lenteur du réseau : sur une connexion mobile lente, économiser 300 kilooctets de JavaScript peut valoir plusieurs centaines de millisecondes.

Il y a toutefois une limite à garder en tête : la compression réduit les octets transférés, pas le travail que le navigateur doit effectuer ensuite. Un fichier JavaScript de 450 kilooctets compressé à 100 est tout de même décompressé en 450 kilooctets, analysé, compilé et exécuté. Le coût sur le thread principal, et donc sur l’INP et le Total Blocking Time, reste inchangé. C’est pourquoi la compression ne remplace pas la réduction du code : elle la complète. Moins de code, et ce peu de code compressé le mieux possible.

Passer de Gzip à Brotli ou à Zstandard vaut en général 15 à 20 % de plus sur les ressources texte. Ce n’est pas un saut comparable à celui qui consiste à passer de l’absence de compression à Gzip, mais sur des pages riches en JavaScript, c’est un gain concret et gratuit, qui ne demande pas de modifier le code du site.

Et après : les dictionnaires de compression

L’évolution la plus intéressante de la compression web concerne les dictionnaires partagés. L’idée est simple : quand un site publie une nouvelle version d’un fichier JavaScript, la majeure partie du contenu est identique à la version précédente, que le navigateur a déjà en cache. Avec le mécanisme Compression Dictionary Transport, le navigateur peut utiliser l’ancienne version comme dictionnaire, et le serveur peut n’envoyer que les différences, compressées en Brotli ou en Zstandard. Le résultat, dans les cas favorables, est une réduction d’un ordre de grandeur par rapport à la compression traditionnelle.

Le mécanisme est pris en charge dans les versions récentes de Chrome et demande une configuration spécifique du serveur ; il intéresse donc surtout aujourd’hui les applications à mises à jour fréquentes et à gros bundles JavaScript. Mais il montre la direction : la compression n’est pas un problème résolu, et les formats modernes comme Zstandard, avec leur prise en charge native des dictionnaires, sont conçus aussi pour ces scénarios.

Comment vérifier

Dans le panneau Network des DevTools, la colonne Size affiche deux valeurs pour chaque ressource : la taille transférée et la taille réelle après décompression. En ajoutant comme colonne l’en-tête de réponse Content-Encoding, on voit d’un coup d’œil quel format a été utilisé pour chaque ressource. Si un fichier JavaScript ou CSS affiche la même taille transférée et réelle, il n’est pas compressé.

En ligne de commande, curl permet de simuler les requêtes du navigateur avec différents en-têtes :

# Quel format le serveur choisit-il ?
curl -sI -H 'Accept-Encoding: zstd, br, gzip' https://example.com/app.js | grep -i 'content-encoding\|vary'

# Combien pèse chaque version ?
for enc in gzip br zstd; do
	printf '%s: ' "$enc"
	curl -s -H "Accept-Encoding: $enc" https://example.com/app.js | wc -c
done

Lighthouse signale les ressources texte non compressées par un avertissement dédié. C’est un contrôle simple, mais il révèle étonnamment souvent des fichiers oubliés : des feuilles de style servies depuis un sous-domaine non configuré, des fichiers JSON de traduction, de gros SVG, des réponses de l’API REST.

Une configuration recommandée

  • Ressources statiques : précompressées au niveau maximal en Brotli et, si le serveur le permet, aussi en Zstandard, avec Gzip en repli.
  • HTML dynamique et API : compression à la volée avec Brotli aux niveaux 4 à 6 ou Zstandard aux niveaux 3 à 6, Gzip 6 en repli.
  • Pages en cache : stockées déjà compressées, à un niveau plus élevé si la génération est rare.
  • Toujours : Vary: Accept-Encoding, pas de compression pour les images, les polices WOFF2 et la vidéo, un seuil minimal d’environ un kilooctet.

En résumé

La compression HTTP réduit de trois quarts ou plus le poids des ressources texte, avec un effet direct sur les temps de chargement et sur les métriques qui en dépendent. Gzip est le standard universel à proposer en repli ; Brotli offre la meilleure compression pour les ressources web, surtout s’il est précompressé au niveau maximal ; Zstandard, désormais pris en charge par la plupart des navigateurs, offre le meilleur équilibre entre taux et vitesse pour la compression à la volée.

La règle pratique est de compresser les statiques au maximum à l’avance, de compresser les dynamiques à des niveaux intermédiaires, de ne pas compresser ce qui l’est déjà, et de vérifier dans les DevTools ce qui arrive réellement au navigateur. Et de se rappeler que la compression réduit les octets, pas le travail : elle reste essentielle, mais elle ne remplace pas un site qui charge moins de code.

L’ÉTAPE SUIVANTE

Commençons
par votre site.

Parlez-nous du projet, des problèmes que vous constatez et de vos objectifs. Ensemble, nous définirons une intervention sur mesure.

Demander une analyse

Vous mène au formulaire de la page Contact.

MANAGED SERVER S.R.L.info@corewebvitals.fr +39 02 5656 9681

Du lundi au vendredi · 9 h 30 – 19 h 30 (CET)

Vous préférez un formulaire en ligne ?
Contactez-nous sur managedserver.it ↗