gzip, Brotli, Zstd Compression HTTP pour les développeurs qui définissent content-encoding : identity par erreur
Comment fonctionne la négociation de compression HTTP (Accept-Encoding / Content-Encoding), un benchmark parallèle de gzip, Brotli et Zstd, comment vérifier que la compression est effectivement activée avec curl, et quatre configurations incorrectes qui désactivent silencieusement la compression.
Votre configuration nginx comporte gzip on;. Votre application retourne du JSON. Le corps de la réponse reste de 35 KB décompressé. Aucune erreur, aucun avertissement — la compression ne se produit simplement pas.
Cela correspond généralement à l'une des quatre erreurs de configuration. Mais d'abord : comment fonctionne le négociation.
Comment fonctionne le négociation de la compression HTTP
Deux en-têtes, rien d'autre. Le client annonce ce qu'il peut décompresser dans Accept-Encoding. Le serveur choisit un algorithme, compresse le corps, et déclare son choix dans Content-Encoding:
GET /api/data HTTP/1.1
Host: example.com
Accept-Encoding: gzip, deflate, br, zstd
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: br
Vary: Accept-Encoding
Le Vary: Accept-Encoding en-tête est non optionnelle si vous souhaitez que le cache CDN soit correct. Sans elle, un CDN pourrait stocker une réponse compressée avec Brotli et la servir à un client qui n'a seulement annoncé gzip dans Accept-Encoding. Ce client tente alors de décompresser Brotli comme gzip et obtient des données corrompues. nginx ajoute cela automatiquement. gzip_vary on; est techniquement valide — cela signifie « aucune compression » — mais personne ne le définit explicitement. Le mode de défaillance réel est l'inverse : aucune
Content-Encoding: identity en-tête au total quand on s'attend à en voir une. Content-Encoding Vérifier que la compression fonctionne effectivement
Avant de déboguer la configuration, confirmez le problème :
envoie
# Check headers only
curl -sI -H "Accept-Encoding: gzip, br, zstd" https://example.com/api/data | grep -i "content-encoding\|vary"
# Compare compressed vs uncompressed size
curl -so /dev/null -w "uncompressed: %{size_download} bytes
" https://example.com/api/data
curl -so /dev/null --compressed -w "compressed: %{size_download} bytes
" https://example.com/api/data
--compressed automatiquement et décompresse la réponse. Si les deux valeurs sont identiques, la compression ne fonctionne pas. Si vous souhaitez inspecter tous les en-têtes de réponse et ce que chacun signifie dans le contexte, le Accept-Encoding: deflate, gzip, br, zstd annotera ces en-têtes, y compris Analyseur d'en-têtes HTTP et les directives cache-control. Vary, Content-Encodinggzip vs Brotli vs Zstd
Trois algorithmes sont pratiquement pertinents pour HTTP aujourd'hui. Les chiffres de référence ci-dessous proviennent des
benchmarks officiels de Zstd sur le corpus Silesia — un ensemble standard de fichiers réels du monde (HTML, code source, PDFs, bases de données), testé sur un processeur Core i7-9700K. Les charges de données purement JSON ou texte brut se compressent généralement mieux. gzip
| Algorithme | Niveau | Ratio | Compresse | Décompresser |
|---|---|---|---|---|
| 1 (rapide) | 2.74x | 69 MB/s | 380 MB/s | 6 (par défaut) |
| 1 (rapide) | 2.97x | 29.9 MB/s | 360 MB/s | 9 (max) |
| 1 (rapide) | 3.10x | 18 MB/s | 380 MB/s | 9 (max) |
| Brotli | 4 | 3.18x | 104 MB/s | 440 MB/s |
| Brotli | 11 (max) | 3.74x | 0.4 MB/s | 440 MB/s |
| Zstd | 2.74x | 2.88x | 430 MB/s | 1,380 MB/s |
| Zstd | 3 (par défaut) | 3.01x | 320 MB/s | 1,350 MB/s |
| Zstd | 19 (max) | 3.40x | 17.5 MB/s | 1,380 MB/s |
1 (rapide) est la base. Le niveau 6 est le bon choix en temps réel — consacrer 65% de CPU de plus pour passer du niveau 6 au niveau 9 vous procure environ 4% de meilleur ratio. Ce n'est pas rentable pour les réponses dynamiques. Les fichiers statiques pré-compressés sont une autre équation.
Brotli dépasse véritablement gzip à coût CPU comparable aux niveaux 4-6, et se décompresse environ 20% plus rapidement. La raison : Brotli dispose d'un dictionnaire statique optimisé pour le contenu web — entités HTML, noms de champs HTTP, mots-clés JavaScript. Il obtient des ratios meilleurs qu'un compresseur généraliste sur le même matériel. Le niveau 11 est uniquement viable pour les fichiers statiques pré-compressés ; à une vitesse de compression de 0,4 MB/s, vous compresserez environ 25 Mo par minute. C'est un pas de construction, pas un gestionnaire de requête.
Zstd est la histoire de vitesse. Le niveau par défaut (3) correspond au ratio de gzip mais compresse 10 fois plus vite et se décompresse presque 4 fois plus vite. La principale limitation est le support des navigateurs : Chrome 118+ (octobre 2023), Firefox 126+ (mai 2024), Safari 18+ (fin 2024). Il n'est pas encore universel pour être utilisé comme algorithme unique, mais si votre serveur négocie correctement, l'ajout de Zstd vous coûte quelques lignes de configuration et aide les clients qui l'annoncent. Zstd au niveau 19 approche le ratio de Brotli-11 sans la pénalité catastrophique de vitesse de compression, le rendant plus utilisable pour le travail en temps réel à forte exigence de compression.
Support des navigateurs et des clients
| Algorithme | Support global pour | Firefox | Safari | Edge | Node.js |
|---|---|---|---|---|---|
| 1 (rapide) | Tous | Tous | Tous | Tous | Intégré (zlib) |
| deflate | Tous | Tous | Tous | Tous | Intégré (zlib) |
| Brotli (br) | 51+ | 44+ | 11+ | 15+ | v10.16+ |
| Zstd | 118+ | 126+ | 18+ | 118+ | v21+ |
Une caractéristique importante : br et zstd apparaissent uniquement dans Accept-Encoding sur des connexions HTTPS. Les navigateurs ne les annoncent pas sur HTTP pur — c'est une défense contre les attaques MITM pouvant injecter des en-têtes de compression. Si vous testez sur http://localhost et que vous ne voyez que gzip, deflate, c'est pourquoi. Testez via HTTPS ou utilisez curl directement (curl ne respecte pas cette restriction).
Quatre erreurs de configuration qui défont silencieusement le système
1. gzip_proxied est manquant (proxy nginx)
nginx’s gzip module compresse les réponses qu'il génère lui-même. Pour les requêtes proxy (application upstream à nginx à client), vous avez besoin de gzip_proxied — sinon, nginx ne compresse que les réponses de ses propres gestionnaires de contenu, pas celles de proxy_pass upstreams.
# This is NOT enough when nginx is a reverse proxy:
gzip on;
gzip_types text/plain application/json application/javascript text/css;
# You need this too:
gzip_proxied any;
La plupart des configurations nginx sont des proxies inverses. La plupart des tutoriels omis gzip_proxied. Ces deux faits expliquent beaucoup de réponses non compressées.
2. Le type MIME n'est pas dans gzip_types
nginx’s par défaut gzip_types est text/html seulement. JSON, CSS, JavaScript, SVG — tous non compressés sauf s'ils sont explicitement listés :
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/rss+xml
image/svg+xml;
nginx compare sur le type MIME de base, donc application/json couvre application/json; charset=utf-8. Aucune nécessité de lister les variantes de charset séparément.
3. Un proxy intermédiaire retire Accept-Encoding
AWS ALB, des workers Cloudflare mal configurés, et certains setups d'API gateway retirent ou modifient Accept-Encoding avant qu'elle ne parvienne à l'origine. Le serveur ne voit jamais cet en-tête, passe à aucune compression, et tous les composants en aval pensent que la fonction est défaillante alors que le problème réel est dans le middleware. Aucune erreur n'apparaît dans la chaîne.
Déboguer en comparant la réponse d'origine avec la réponse du CDN :
# Via CDN/proxy
curl -sI -H "Accept-Encoding: gzip, br" https://example.com/api/data
# Direct to origin (bypassing CDN via --resolve or direct IP)
curl -sI -H "Accept-Encoding: gzip, br" --resolve "example.com:443:ORIGIN_IP" https://example.com/api/data
Si l'origine retourne Content-Encoding: gzip directement mais que la réponse du CDN n'a pas Content-Encoding, le CDN retire quelque chose — plus probablement retire Accept-Encoding en entrée, de sorte que l'origine ne compresse jamais en premier lieu.
4. L'application upstream compresse, puis nginx essaie de compresser à nouveau
Si votre application Node.js/Go/Python compresse le corps de la réponse et définit Content-Encoding: gzip, nginx devrait ignorer la double compression — mais cela dépend du timing de l'en-tête. Si l'upstream envoie l'en-tête en cours de flux ou si la détection de nginx se produit en concurrence, vous pouvez obtenir un contenu doublement compressé que les clients ne parviennent pas à décoder.
La solution propre : laissez nginx gérer toute la compression. Supprimez le middleware de compression de votre application (module compression d'express, module gzip.Handlerde Go, etc.), retournez des réponses brutes, et laissez nginx compresser à l'edge. Mêmes gains de performance, sans risque de double compression.
Configurations fonctionnelles
nginx
gzip on;
gzip_vary on; # adds Vary: Accept-Encoding automatically
gzip_proxied any; # compress responses from proxied upstreams
gzip_comp_level 6;
gzip_min_length 256; # skip tiny responses where overhead isn't worth it
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/rss+xml
image/svg+xml;
# Brotli requires the ngx_brotli module
# https://github.com/google/ngx_brotli
brotli on;
brotli_comp_level 4;
brotli_static on; # serve pre-compressed .br files when they exist
brotli_types
text/plain
text/css
application/json
application/javascript
image/svg+xml;
Apache
LoadModule deflate_module modules/mod_deflate.so
AddOutputFilterByType DEFLATE text/html text/plain text/css application/json application/javascript image/svg+xml
Header append Vary Accept-Encoding
# mod_brotli requires Apache 2.4.26+
LoadModule brotli_module modules/mod_brotli.so
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css application/json application/javascript image/svg+xml
Caddy
Caddy active gzip et Brotli par défaut. Pour ajouter Zstd explicitement :
example.com {
encode gzip zstd br
reverse_proxy localhost:3000
}
Aucune liste de type MIME, aucune gzip_proxied cas particuliers, gestion correcte Vary de base. La réponse honnête à la question « quel serveur a la surface la plus faible de configuration liée à la compression » est Caddy.
Test de la compression sur vos propres charges
Les chiffres de référence sur le corpus Silesia vous indiquent les performances relatives, mais votre charge spécifique importe davantage. Une réponse JSON répétitive avec des noms de champs constants se comprime différemment d'un JavaScript minifié ou d'un HTML mixte. Ces outils vous permettent de tester des charges spécifiques dans le navigateur sans déployer un serveur local de compression :
- Testeur Gzip / Zlib / Deflate — collez votre charge, voyez immédiatement la taille compressée et le ratio
- Encodeur/Décodeur de compression Brotli — testez la compression Brotli à différents niveaux de qualité
- Outil de compression Zstandard (Zstd) — encodez/décodez Zstd dans le navigateur
Utile pour décider si vous pré-comprimez les assets statiques à Brotli-11 ou laissez nginx gérer la compression en temps réel avec gzip. Collez votre réponse réelle, comparez les ratios, et prenez une décision avec des chiffres réels.
En résumé
Si les réponses ne sont pas compressées et curl -sI confirme l'absence de Content-Encoding, la solution est presque certainement l'une des quatre erreurs de configuration ci-dessus — le plus probablement gzip_proxied any; pour nginx, ou un CDN qui consomme votre Accept-Encoding en-tête. Vérifiez directement l'origine avant de blâmer votre configuration serveur.
Pour le choix de l'algorithme : gzip-6 est acceptable pour les réponses dynamiques API et présente presque aucun risque de configuration. Ajoutez Brotli pour les assets statiques — pré-comprimez au niveau 11 lors de votre étape de build, servez avec brotli_static on, et laissez nginx passer à gzip pour les clients qui n'ont pas annoncé br. Zstd vaut l'ajout maintenant ; le coût de configuration est négligeable et sa présence dans les navigateurs augmente rapidement. Offrir les trois algorithmes avec une gestion correcte Vary: Accept-Encoding est la bonne posture pour tout nouveau projet.
Installez nos extensions
Ajoutez des outils IO à votre navigateur préféré pour un accès instantané et une recherche plus rapide
恵 Le Tableau de Bord Est Arrivé !
Tableau de Bord est une façon amusante de suivre vos jeux, toutes les données sont stockées dans votre navigateur. D'autres fonctionnalités arrivent bientôt !
Outils essentiels
Tout voir Nouveautés
Tout voirMise à jour: Notre dernier outil a été ajouté le 19 juin 2026
