Compression HTTP · 12 min read · Sep 15, 2025
mod_gzip - servir du contenu compressé par le serveur web Apache - Page 9
Auteur : Michael Schröpl
Compression via négociation
Utiliser une fonction de compression configurable comme mod_gzip doit toujours être une sorte de négociation de contenu, c’est-à-dire servir différents contenus de manière conditionnelle pour la même URL demandée, en fonction d’informations spécifiques dans les en-têtes HTTP.
D’autre part, HTTP permet le stockage temporaire des réponses aux requêtes HTTP dans des caches, surtout lors de l’utilisation de serveurs proxy. Si maintenant
- un client HTTP envoie une requête,
- la réponse correspondante est servie sous forme compressée et stockée par un proxy et
- par la suite, un autre client HTTP soumet une requête pour l’URL en question,
alors le serveur proxy - ne possédant pas d’informations supplémentaires - a un problème :
- Est-il en droit de servir le contenu mis en cache à ce deuxième client HTTP également, ou
- doit-il transférer la requête au serveur HTTP ?
Car seul le serveur HTTP peut finalement déterminer (en fonction de sa configuration contenant les règles de filtrage correspondantes) si le deuxième client HTTP peut également recevoir des données de réponse compressées.
Au fait, ce n’est pas un effet de l’utilisation d’une procédure de compression seule, mais un problème général concernant la mise en cache des données HTTP dont le contenu ne peut pas être spécifié de manière univoque par une URL dans le cache d’un serveur proxy ou des serveurs similaires équipés de mémoire dans le chemin de transport. Cela inclut les procédures de négociation de tous types ainsi que la soumission d’informations supplémentaires dans les en-têtes HTTP, comme les données d’authentification ou les cookies.
Exigences de performance
Bien sûr, on peut essayer d’éviter le problème en niant explicitement de mettre en cache les données de réponse correspondantes (en utilisant les en-têtes HTTP correspondants Expires : et Pragma : dans HTTP/1.0 et Cache-Control : dans HTTP/1.1) à tous les serveurs proxy existants sur le chemin entre le client et le serveur.
Mais l’objectif de la compression est d’accélérer le transfert de données (en réduisant le volume de données) - et la mise en cache des données sert le même objectif (en réduisant les accès au serveur HTTP). Et une optimisation de performance ne devrait pas conduire à ce qu’une autre ne soit plus utilisable, surtout que ces deux ne se remplacent pas mais peuvent effectivement se compléter dans le cas en question.
Informations sur les paramètres de négociation
La spécification HTTP contient la définition de l’en-tête HTTP Vary où le serveur HTTP peut informer le serveur proxy sur
- si la réponse était le résultat unique d’une requête URL ou
- si d’autres attributs d’une requête pour la même URL pouvaient conduire à des résultats différents.
Sa valeur peut contenir une liste de noms d’autres en-têtes HTTP dont le contenu a été pertinent pour servir cette réponse très particulière pour une requête. Ainsi, le serveur HTTP peut même informer le serveur proxy sur quels en-têtes HTTP ont influencé la décision concernant le contenu servi.
Lorsqu’un serveur proxy transfère une requête à un serveur HTTP et souhaite stocker la réponse dans son cache plus tard, il doit encore être en possession des en-têtes HTTP de la requête originale lorsque la réponse du serveur HTTP arrive.
Maintenant, si le serveur HTTP marque un contenu conditionnel d’une réponse par l’en-tête Vary correspondant, alors
- le serveur proxy doit stocker dans son cache non seulement le contenu de cette réponse mais aussi toutes les informations pertinentes des en-têtes HTTP (dont les noms ont été énumérés dans la liste de valeurs de l’en-tête HTTP Vary de la réponse) de la requête, et
- il ne doit pas servir ce contenu mis en cache comme réponse à d’autres requêtes à moins que les informations des en-têtes HTTP correspondants d’une telle requête subséquente correspondent au moins à celles de la requête originale, c’est-à-dire soient sémantiquement identiques aux valeurs de la requête originale pour chacun de ces en-têtes.
Restrictions résultantes pour le serveur HTTP
Les explications précédentes ont montré comment un serveur proxy peut gérer correctement la livraison conditionnelle des réponses HTTP (étant le résultat d’une négociation de contenu) tout en maximisant l’utilisation de son effet de mise en cache en même temps - à condition que
- le serveur HTTP fournisse au serveur proxy suffisamment d’informations sur les paramètres de négociation et
- le serveur proxy soit en possession des informations correspondantes en cas d’une requête subséquente pour la même URL par un autre client HTTP.
Mais cela signifie maintenant une restriction pour les degrés de liberté du processus de négociation. Car si le serveur proxy doit décider s’il peut servir son contenu de cache ou non exclusivement sur la base des informations dans une requête HTTP, alors les règles de négociation des serveurs HTTP ne doivent pas faire référence à autre chose que le contenu des en-têtes HTTP !
Mais malheureusement, cette condition préalable n’est pas remplie par mod_gzip, car parmi les six classes de règles de filtrage fournies,
- deux ‘légales’ (reqheader et uri) se réfèrent exclusivement au contenu des en-têtes HTTP mais
- quatre autres ‘illégales’ (rspheader, handler, file et mime) se réfèrent à des informations qui ne seront disponibles que lors de l’évaluation de la requête par le serveur HTTP.
Ainsi, si un serveur amélioré par mod_gzip utilise l’une de ces règles de filtrage ‘illégales’, alors le serveur proxy ne pourra plus décider correctement de l’applicabilité de son contenu de cache pour répondre à d’autres requêtes.
Ce n’est pas non plus d’un grand secours pour le serveur proxy si mod_gzip notifie le serveur proxy d’être manifestement surchargé (en fournissant une liste complète des classes de règles de filtrage significatives pour cette requête dans un en-tête Vary : si cela était même légal). Tout ce que le serveur proxy pourrait faire est d’utiliser l’occurrence de l’une de ces quatre classes de règles de filtrage ‘illégales’ comme critère pour ne pas mettre en cache le contenu de la réponse.
Cela ne serait pas si mal - tant que le serveur HTTP se limite à utiliser uniquement des règles ‘légales’, il serait en mesure de coopérer de manière optimale avec un serveur proxy.
Mais malheureusement, cela est impossible avec mod_gzip 1.3.19.1a.
L’intégration de mod_gzip 1.3.19.1a dans l’architecture Apache 1.3 se fait de manière relativement complexe :
- Dans la phase de traitement 1, le traitement mod_gzip vérifie s’il doit s’intéresser à la gestion des résultats de cette requête et se préparer pour cela - en fonction des règles de quatre classes (reqheader, uri, file et handler, c’est-à-dire deux classes de règles ‘légales’ et deux ‘illégales’)
- Dans la phase de traitement 2, mod_gzip vérifie s’il doit maintenant réellement compresser le contenu de la réponse (maintenant disponible) - en fonction des règles de deux classes (rspheader et mime, toutes deux des classes de règles ‘illégales’).
Pour l’autorisation réussie d’une requête de compression, au moins la satisfaction d’une règle include d’une des deux phases est requise (et la non-satisfaction de toutes les règles exclude).
Mais comme les deux classes de règles include de la phase 2 sont ‘illégales’, chaque liste de classes de règles de filtrage pertinentes pour une compression réussie dans l’implémentation actuelle de mod_gzip doit au moins couvrir une classe de règle ‘illégale’.
Ainsi, il est impossible de fournir à un serveur proxy des informations qu’il peut utiliser pour décider de l’applicabilité de certains contenus de cache - les informations soumises dépasseront toujours la compréhension du serveur proxy.
En-têtes Vary dans mod_gzip 1.3.19.2a et ultérieur
À partir de la version 1.3.19.2a, mod_gzip envoie des en-têtes Vary : - pour chaque requête où le module a été impliqué au moins une fois (qu’il s’agisse ou non de données compressées servies).
À cet état de recherche pour mod_gzip, chaque requête (qu’elle ait ou non été servie sous forme compressée) est potentiellement une négociation :
- au moins sur l’en-tête HTTP Accept-Encoding, et
- possiblement sur d’autres en-têtes HTTP également (à savoir tous ceux qui apparaissent dans les règles de filtrage de la classe reqheader)
À partir de maintenant, mod_gzip n’est pas encore capable de générer le meilleur ensemble possible, c’est-à-dire le minimum d’en-têtes Vary : requis - pour cela, il serait nécessaire de réécrire complètement la procédure d’évaluation des règles de mod_gzip.
Comme première étape, le module depuis la version 1.3.19.2a envoie un en-tête Vary : qui contient
- la valeur Accept-Encoding ainsi que
- les noms de chaque en-tête utilisé dans n’importe quelle règle reqheader,
car chacune de ces règles pourrait faire la différence pour le résultat de la négociation, et dans chacun de ces cas, le résultat dépendrait des valeurs des en-têtes HTTP reçus. Dans certains cas, cela peut être beaucoup trop (et alors entraver massivement la mise en cache efficace du contenu), mais au moins c’est quelque chose avec quoi commencer.
En tant qu’amélioration de cette stratégie, mod_gzip 1.3.26.1a n’envoie aucun en-tête Vary : si la compression de la requête en question a été refusée en raison d’une règle mod_gzip_item_exclude de type
- file,
- uri ou
- handler
car l’évaluation de cette règle ne peut pas avoir dépendu des en-têtes HTTP reçus, et donc dans ces cas, aucune négociation (sur des dimensions qui pourraient contenir des valeurs différentes pour différentes requêtes HTTP) n’a eu lieu.
Si vous souhaitez qu’aucun en-tête Vary : ne soit envoyé pour des fichiers que vous êtes sûr de ne jamais servir sous forme compressée en raison de d’autres règles de configuration, vous devrez désactiver mod_gzip pour ceux-ci.
Un exemple pour ne pas envoyer d’en-têtes Vary : pour des images GIF qui pourraient être mises en cache par un proxy comme Squid 2.4 pourrait ressembler à ceci :
mod_gzip_on No
Pour les versions à venir, les tâches suivantes restent ouvertes :
- Reconnaître dans tous les cas possibles que la réaction à la requête actuelle ne peut jamais entraîner la livraison de données compressées parce qu’une règle mod_gzip_item_exclude indépendante des attributs de la requête est déclenchée.
- Reconnaître qu’une négociation a eu lieu qui ne peut pas être décrite par une liste de noms d’en-têtes HTTP - dans ce cas, Vary : * doit être envoyé (et la documentation pour mod_gzip doit explicitement indiquer que ces directives ne doivent être utilisées que si absolument nécessaire, car leur utilisation aura un effet négatif sur le travail des proxies de mise en cache).
- Vérifier si des constellations sont possibles où seul un sous-ensemble de noms d’en-têtes de toutes les règles reqheader est requis dans un en-tête Vary : - moins il y a de noms, moins il y a de variantes à stocker dans le cache proxy en parallèle.
Négociation sur d’autres dimensions que les en-têtes HTTP
Dans des cas très spéciaux, c’est-à-dire lors de l’utilisation de certaines directives de configuration, une négociation est effectuée par mod_gzip sur des dimensions qui ne peuvent même pas être exprimées en termes de noms d’en-têtes HTTP. Cela s’applique aux directives
- mod_gzip_min_http (version HTTP minimale requise) ainsi que
- mod_gzip_handle_methods (méthodes HTTP à gérer)
Dans les deux cas, mod_gzip ne peut pas expliquer à un proxy ce qui a été fait en disant les noms des en-têtes HTTP. La réaction appropriée selon la spécification HTTP/1.1 est d’envoyer l’en-tête Vary : *.
mod_gzip 1.3.26.1a envoie un en-tête Vary : ** si la directive mod_gzip_min_http* a été utilisée.
Quant à la directive mod_gzip_handle_methods, il semble actuellement qu’il ne soit pas encore absolument clair si deux requêtes HTTP pour la même URI mais utilisant différentes méthodes HTTP demandent effectivement la même entité HTTP - cela décidera si un en-tête Vary : * devra être envoyé lors de l’utilisation de cette directive également, et sera un problème à résoudre dans les prochaines versions.
Mais comme dans ce cas un serveur proxy ne peut pas comprendre le type de négociation effectuée, il n’est pas en droit de stocker des réponses portant cette marque dans un cache.
Ainsi, l’utilisation de l’une de ces directives désactive complètement la mise en cache proxy de chaque réponse envoyée par ce serveur HTTP, qu’elle soit compressée ou non. Par conséquent, nous vous conseillons de ne plus utiliser l’une de ces directives.
Le UserAgent comme cas spécial
Stocker des variantes de différents paramètres de négociation en parallèle dans un cache proxy peut être raisonnable si seulement quelques valeurs possibles peuvent réellement se produire - comme dans le cas de Content-Encoding. S’il y a un grand nombre de valeurs possibles, alors un stockage parallèle de variantes n’est plus faisable.
C’est exactement ce qui s’applique au nom UserAgent comme identification du client HTTP. Chaque sous-version d’un navigateur envoie une chaîne UserAgent complexe qui contient non seulement le nom et la version des navigateurs mais aussi d’autres informations (langue nationale, nom et version du système d’exploitation, etc.). Il existe des centaines de chaînes UserAgent connues - et au-delà, un certain nombre de mécanismes pour manipuler cette chaîne UserAgent. Certains navigateurs (comme Opera) permettent même à l’utilisateur de sélectionner explicitement le contenu de cette chaîne UserAgent pour se faire passer pour un autre navigateur (car de nombreux créateurs de pages web techniquement incompétents construisent leur site en fonction du nom d’un navigateur et excluent inutilement certains navigateurs, ou simplement parce que leur utilisateur ne veut pas montrer inutilement des détails sur l’équipement informatique qu’il utilise, pour préserver sa vie privée).
À quel point il peut être raisonnable de servir des pages web compressées de manière conditionnelle sur l’identité d’un client HTTP dans certains cas (comme en ce qui concerne les nombreux bugs de Netscape 4), le revers de l’utilisation des chaînes UserAgent comme base d’une négociation HTTP sera que le contenu de cet en-tête HTTP, d’une part, varie trop pour tirer des conclusions fiables et, d’autre part, contient trop de valeurs différentes pour qu’un proxy de mise en cache puisse jamais conserver en parallèle les résultats des requêtes pour la même URL pour toutes ces variantes de négociation.
À partir de la version 1.3.19.2a, mod_gzip envoie un en-tête Vary : décrivant l’en-tête HTTP User-Agent : comme paramètre de la négociation, si une directive correspondante a été utilisée dans la configuration. Mais la probabilité qu’une requête successive contienne une valeur User-Agent : exactement identique (de sorte que ce client puisse donc recevoir le contenu déjà stocké) est très faible.
En réalité, le serveur HTTP traiterait même de grands ensembles de UserAgents (qui sont supposés être fonctionnellement équivalents en raison de sa configuration) de manière identique lors de la négociation - mais l’en-tête Vary : ne permet pas au serveur HTTP de dire au proxy de mise en cache quelles parties des chaînes UserAgent ont été évaluées par le serveur HTTP comme contenu significatif lors de la négociation. Le serveur proxy ne peut que savoir que le UserAgent a joué un rôle - et en étant conscient de cela, le proxy doit traiter les UserAgents individuels comme étant différents même si le serveur HTTP ne se comporterait pas ainsi.
Ainsi, l’utilisation de règles de filtrage évaluant l’en-tête HTTP UserAgent conduira à désactiver totalement toute mise en cache pour les paquets de réponse créés de cette manière. L’utilisateur de mod_gzip doit être absolument conscient de cet effet - et donc utiliser d’autres méthodes de filtrage (ayant un nombre plus réduit de valeurs différentes possibles) si cela est possible, pour fournir le même type de différenciation entre ces clients HTTP.
Emplacement original de ce document :
Recevez de nouveaux articles dans votre boîte de réception.
Aucun spam. Désabonnez-vous à tout moment.