Kompression · 10 min read · Sep 15, 2025

mod_gzip - komprimierte Inhalte durch den Apache-Webserver bereitstellen - Seite 9

Autor: Michael Schröpl

Kompression durch Verhandlung

Die Verwendung einer konfigurierbaren Kompressionsfunktion wie mod_gzip muss letztendlich immer eine Art von Inhaltsverhandlung sein, d. h. bedingte Bereitstellung unterschiedlicher Inhalte für dieselbe angeforderte URL, abhängig von spezifischen Informationen in den HTTP-Headern.

Andererseits erlaubt HTTP die vorübergehende Speicherung von Antworten auf HTTP-Anfragen in Caches, insbesondere bei der Verwendung von Proxy-Servern. Wenn nun

  1. ein HTTP-Client eine Anfrage sendet,
  2. die entsprechende Antwort in komprimierter Form bereitgestellt und von einem Proxy gespeichert wird und
  3. anschließend ein anderer HTTP-Client eine Anfrage für die betreffende URL stellt,

dann hat der Proxy-Server - der nicht im Besitz weiterer Informationen ist - ein Problem:

  • Ist er berechtigt, den zwischengespeicherten Inhalt auch diesem zweiten HTTP-Client bereitzustellen, oder
  • muss er die Anfrage an den HTTP-Server weiterleiten?

Denn nur der HTTP-Server kann letztendlich herausfinden (basierend auf seiner Konfiguration, die die entsprechenden Filterregeln enthält), ob der zweite HTTP-Client auch komprimierte Antwortdaten erhalten darf.

Übrigens ist dies kein Effekt der Verwendung eines Kompressionsverfahrens allein, sondern ein allgemeines Problem bei der Zwischenspeicherung von HTTP-Daten, deren Inhalt nicht eindeutig durch eine URL innerhalb des Caches eines Proxy-Servers oder ähnlicher speicherbasierter Server auf der Transportstrecke spezifiziert werden kann. Dies umfasst Verhandlungsverfahren jeglicher Art sowie das Einreichen zusätzlicher Informationen in den HTTP-Headern, wie Authentifizierungsdaten oder Cookies.

Leistungsanforderungen

Natürlich kann man versuchen, das Problem zu vermeiden, indem man explizit verweigert, die entsprechenden Antwortdaten zu cachen (indem man die entsprechenden HTTP-Header Expires: und Pragma: in HTTP/1.0 und Cache-Control: in HTTP/1.1 verwendet) für alle Proxy-Server, die sich auf dem Weg zwischen Client und Server befinden.

Aber das Ziel der Kompression ist es, die Datenübertragung zu beschleunigen (durch Reduzierung des Datenvolumens) - und das Caching von Daten dient demselben Ziel (durch Reduzierung der Zugriffe auf den HTTP-Server). Und eine Leistungsoptimierung sollte nicht dazu führen, dass eine andere nicht mehr nutzbar ist, insbesondere da diese beiden sich nicht gegenseitig ersetzen, sondern im betreffenden Fall effektiv einander ergänzen können.

Informationen über Verhandlungsparameter

Die HTTP-Spezifikation enthält die Definition des Vary HTTP-Headers, in dem der HTTP-Server den Proxy-Server darüber informieren kann,

  • ob die Antwort das einzigartige Ergebnis einer URL-Anfrage war oder
  • ob andere Attribute einer Anfrage für dieselbe URL zu unterschiedlichen Ergebnissen führen könnten.

Sein Wert kann eine Liste von Namen anderer HTTP-Header enthalten, deren Inhalt für die Bereitstellung dieser speziellen Antwort auf eine Anfrage relevant war. So kann der HTTP-Server sogar den Proxy-Server darüber informieren, welche HTTP-Header die Entscheidung über den bereitgestellten Inhalt beeinflusst haben.

Wenn ein Proxy-Server eine Anfrage an einen HTTP-Server weiterleitet und die Antwort später in seinem Cache speichern möchte, sollte er dennoch im Besitz der HTTP-Header der ursprünglichen Anfrage sein, wenn die Antwort des HTTP-Servers eintrifft.

Wenn der HTTP-Server nun einen bedingten Inhalt einer Antwort durch den entsprechenden Vary-Header kennzeichnet, dann

  • muss der Proxy-Server in seinem Cache nicht nur den Inhalt dieser Antwort speichern, sondern auch alle relevanten Informationen der HTTP-Header (deren Namen in der Werteliste des Vary-HTTP-Headers der Antwort aufgezählt wurden) aus der Anfrage, und
  • er darf diesen zwischengespeicherten Inhalt nicht als Antwort auf weitere Anfragen bereitstellen, es sei denn, die Informationen der entsprechenden HTTP-Header einer solchen nachfolgenden Anfrage stimmen mindestens ‘überein’ mit denen der ursprünglichen Anfrage, d. h. sind semantisch identisch mit den Werten der ursprünglichen Anfrage für jeden dieser Header.

Ergebnisbeschränkungen für den HTTP-Server

Die vorherigen Erklärungen haben gezeigt, wie ein Proxy-Server die bedingte Bereitstellung von HTTP-Antworten (die das Ergebnis einer Inhaltsverhandlung sind) korrekt und mit maximaler Ausnutzung seines Caching-Effekts gleichzeitig handhaben kann - vorausgesetzt,

  • der HTTP-Server stellt dem Proxy-Server ausreichende Informationen über die Verhandlungsparameter zur Verfügung und
  • der Proxy-Server ist im Besitz der entsprechenden Informationen im Falle einer nachfolgenden Anfrage an dieselbe URL durch einen anderen HTTP-Client.

Aber letzteres bedeutet nun eine Einschränkung für die Freiheitsgrade des Verhandlungsprozesses. Denn wenn der Proxy-Server entscheiden muss, ob er seinen Cache-Inhalt oder nicht ausschließlich basierend auf Informationen innerhalb einer HTTP-Anfrage bereitstellen darf, dann dürfen die Verhandlungsregeln der HTTP-Server sich nicht auf etwas anderes als den Inhalt der HTTP-Header beziehen!

Aber leider wird diese Voraussetzung von mod_gzip nicht erfüllt, da von den sechs Klassen von Filterregeln,

  • zwei ‘legale’ ( reqheader und uri) sich ausschließlich auf den Inhalt der HTTP-Header beziehen, aber
  • vier andere ‘illegale’ ( rspheader, handler, file und mime) sich auf Informationen beziehen, die nur während der Auswertung der Anfrage durch den HTTP-Server verfügbar sind.

Wenn ein mod_gzip-verbesserter Server eine dieser ‘illegalen’ Filterregeln verwendet, dann kann der Proxy-Server nicht mehr korrekt entscheiden, ob sein Cache-Inhalt für die Beantwortung weiterer Anfragen anwendbar ist.

Dabei hilft es dem Proxy-Server auch nicht viel, wenn mod_gzip den Proxy-Server darüber informiert, dass er offensichtlich überfordert ist (indem er eine vollständige Liste der für diese Anfrage signifikanten Filterregelklassen innerhalb eines Vary: Headers bereitstellt, wenn das überhaupt legal wäre). Alles, was der Proxy-Server tun könnte, wäre, das Auftreten einer dieser vier ‘illegalen’ Filterregelklassen als Kriterium zu verwenden, um den Inhalt der Antwort nicht zu cachen.

Das allein wäre nicht so schlimm - solange der HTTP-Server sich darauf beschränkt, nichts als ‘legale’ Regeln zu verwenden, könnte er optimal mit einem Proxy-Server zusammenarbeiten.

Aber leider ist dies mit mod_gzip 1.3.19.1a unmöglich.

Die Einbettung von mod_gzip 1.3.19.1a in die Apache 1.3-Architektur erfolgt auf relativ komplexe Weise:

  • In Verarbeitungsphase 1 prüft mod_gzip, ob es überhaupt an der Bearbeitung der Ergebnisse dieser Anfrage interessiert sein sollte und bereitet sich darauf vor - basierend auf den Regeln von vier Klassen ( reqheader, uri, file und handler, d. h. zwei ‘legale’ und zwei ‘illegale’ Regelklassen)
  • In Verarbeitungsphase 2 prüft mod_gzip, ob es nun tatsächlich den (nun verfügbaren) Antwortinhalt komprimieren sollte - basierend auf den Regeln von zwei Klassen ( rspheader und mime, beide ‘illegale’ Regelklassen).

Für die erfolgreiche Genehmigung einer Anfrage zur Kompression ist mindestens die Erfüllung einer include-Regel aus einer der beiden Phasen erforderlich (und die Nichterfüllung aller exclude-Regeln).

Aber da beide include-Regelklassen aus Phase 2 ‘illegal’ sind, muss jede Liste relevanter Filterregelklassen für eine erfolgreiche Kompression in der aktuellen mod_gzip-Implementierung mindestens eine ‘illegale’ Regelklasse abdecken.

Somit ist es unmöglich, einem Proxy-Server Informationen bereitzustellen, die er zur Entscheidung über die Anwendbarkeit eines Cache-Inhalts verwenden kann - die übermittelten Informationen werden immer die Auffassung des Proxy-Servers übersteigen.

Vary-Header in mod_gzip 1.3.19.2a und höher

Beginnend mit Version 1.3.19.2a sendet mod_gzip Vary: Header - für jede einzelne Anfrage, an der das Modul mindestens einmal beteiligt war (unabhängig davon, ob komprimierte Daten bereitgestellt wurden oder nicht).

In diesem Stand der Forschung für mod_gzip ist jede Anfrage (unabhängig davon, ob die Antwort tatsächlich in komprimierter Form bereitgestellt wurde oder nicht) potenziell eine Verhandlung:

  • mindestens über den Accept-Encoding HTTP-Header, und
  • möglicherweise auch über andere HTTP-Header (nämlich alle, die in Filterregeln der Klasse reqheader vorkommen)

Bis jetzt ist mod_gzip noch nicht in der Lage, die bestmöglichen, d. h. das Minimum an erforderlichen Vary: Headern zu generieren - dafür wäre es notwendig, das Regelbewertungsverfahren von mod_gzip vollständig umzuschreiben.

Als ersten Schritt sendet das Modul seit Version 1.3.19.2a einen Vary: Header, der enthält

  • den Wert Accept-Encoding sowie
  • die Namen von jedem Header, der in irgendeiner reqheader-Regel verwendet wird,

da jede dieser Regeln den Unterschied für das Ergebnis der Verhandlung ausmachen könnte, und in jedem dieser Fälle würde das Ergebnis von den Werten der empfangenen HTTP-Header abhängen. In bestimmten Fällen kann dies viel zu viel sein (und dann massiv die effiziente Zwischenspeicherung von Inhalten behindern), aber es ist zumindest ein Anfang.

Als Verbesserung dieser Strategie sendet mod_gzip 1.3.26.1a keinen Vary: Header, wenn die Kompression der betreffenden Anfrage aufgrund einer mod_gzip_item_exclude-Regel der

  • file,
  • uri bzw.
  • handler

Art abgelehnt wurde - da die Auswertung dieser Regel nicht von den empfangenen HTTP-Headern abhängig gewesen sein kann, und daher in diesen Fällen tatsächlich keine Verhandlung (über Dimensionen, die unterschiedliche Werte für verschiedene HTTP-Anfragen enthalten könnten) stattgefunden hat.

Wenn Sie keine Vary: Header für Dateien senden möchten, von denen Sie sicher sind, dass sie aufgrund anderer Konfigurationsregeln niemals in komprimierter Form bereitgestellt werden, müssten Sie mod_gzip in diesen Fällen deaktivieren.

Ein Beispiel dafür, keine Vary: Header für GIF-Bilder zu senden, die von einem Proxy wie Squid 2.4 zwischengespeichert werden könnten, könnte so aussehen:

  
 mod_gzip_on No  

Für kommende Versionen bleiben folgende Aufgaben offen:

  • Erkennen in allen möglichen Fällen, dass die Reaktion auf die aktuelle Anfrage niemals dazu führen kann, dass komprimierte Daten bereitgestellt werden, weil eine mod_gzip_item_exclude-Regel unabhängig von den Attributen der Anfrage greift.
  • Erkennen, dass eine Verhandlung stattgefunden hat, die nicht durch eine Liste von HTTP-Headernamen beschrieben werden kann - in diesem Fall Vary: * sollte gesendet werden (und die Dokumentation für mod_gzip sollte ausdrücklich darauf hinweisen, dass diese Direktiven nur verwendet werden sollten, wenn es absolut erforderlich ist, da deren Verwendung negative Auswirkungen auf die Arbeit von Caching-Proxys haben wird).
  • Überprüfen, ob Konstellationen möglich sind, bei denen nur eine Teilmenge von Headernamen aus allen reqheader-Regeln in einem Vary: Header erforderlich ist - je weniger Namen vorhanden sind, desto weniger Varianten müssen parallel im Proxy-Cache gespeichert werden.

Verhandlung über andere Dimensionen als HTTP-Header

In ganz speziellen Fällen, d. h. bei Verwendung bestimmter Konfigurationsdirektiven, erfolgt eine Verhandlung durch mod_gzip über Dimensionen, die nicht einmal in Bezug auf HTTP-Headernamen ausgedrückt werden können. Dies gilt für die Direktiven

  • mod_gzip_min_http (minimale erforderliche HTTP-Version) sowie
  • mod_gzip_handle_methods (zu behandelnde HTTP-Methoden)

In beiden Fällen kann mod_gzip einem Proxy nicht erklären, was getan wurde, indem es die Namen von HTTP-Headern angibt. Die angemessene Reaktion gemäß der HTTP/1.1-Spezifikation ist das Senden des Vary: * HTTP-Headers.

mod_gzip 1.3.26.1a sendet einen Vary: ** Header, wenn die Direktive mod_gzip_min_http* verwendet wurde.

Was die Direktive mod_gzip_handle_methods betrifft, scheint es derzeit noch nicht absolut klar zu sein, ob zwei HTTP-Anfragen für dieselbe URI, aber unter Verwendung unterschiedlicher HTTP-Methoden tatsächlich nach demselben HTTP-Entität fragen - dies wird entscheiden, ob ein Vary: * Header auch bei Verwendung dieser Direktive gesendet werden muss, und ist ein Thema, das in zukünftigen Versionen gelöst werden muss.

Da in diesem Fall ein Proxy-Server die Art der durchgeführten Verhandlung nicht verstehen kann, ist er nicht berechtigt, Antworten, die dieses Zeichen tragen, in einem Cache zu speichern.

Daher deaktiviert die Verwendung einer dieser Direktiven vollständig das Caching jeder einzelnen Antwort, die von diesem HTTP-Server gesendet wird, unabhängig davon, ob sie in komprimierter oder unkomprimierter Form vorliegt. Daher raten wir Ihnen, keine dieser Direktiven mehr zu verwenden.

Der UserAgent als Sonderfall

Das parallele Speichern von Varianten unterschiedlicher Verhandlungsparameter in einem Proxy-Cache kann sinnvoll sein, wenn nur wenige mögliche Werte tatsächlich auftreten können - wie im Fall von Content-Encoding. Wenn es eine große Anzahl möglicher Werte gibt, ist ein paralleles Speichern von Varianten nicht mehr praktikabel.

Genau dies gilt für den UserAgent-Namen als Identifikation des HTTP-Clients. Jede Unterversion eines Browsers sendet einen komplexen UserAgent-String, der nicht nur den Namen und die Version der Browser, sondern auch weitere Informationen (Landessprache, Betriebssystemname und -version usw.) enthält. Es gibt Hunderte von bekannten UserAgent-Strings - und darüber hinaus eine Reihe von Mechanismen zur Manipulation dieses UserAgent-Strings. Einige Browser (wie Opera) erlauben es sogar dem Benutzer, den Inhalt dieser UserAgent-Strings explizit auszuwählen, um sich als ein anderer Browser auszugeben (da viele technisch inkompetente Webseiten-Ersteller ihre Seite auf den Namen eines Browsers aufbauen und unnötig einige Browser ausschließen, oder einfach nur, weil ihr Benutzer nicht unnötig Details über die Computerhardware, die er verwendet, zeigen möchte, um seine/ihre Privatsphäre zu wahren).

Wie sinnvoll die bedingte Bereitstellung komprimierter Webseiten auf der Grundlage der Identität eines HTTP-Clients in einigen Fällen sein mag (wie in Bezug auf die zahlreichen Bugs von Netscape 4), wird der Nachteil der Verwendung der UserAgent-Strings als Grundlage einer HTTP-Verhandlung darin bestehen, dass der Inhalt dieses HTTP-Headers einerseits zu variabel ist, um zuverlässige Schlussfolgerungen daraus zu ziehen, und andererseits zu viele verschiedene Werte enthält, sodass kein Caching-Proxy jemals in der Lage sein wird, die Ergebnisse von Anfragen für dieselbe URL für all diese Verhandlungsvarianten parallel zu speichern.

Ab Version 1.3.19.2a sendet mod_gzip einen Vary: Header, der den HTTP-Header User-Agent: als Parameter der Verhandlung beschreibt, wenn eine entsprechende Direktive in der Konfiguration verwendet wurde. Aber die Wahrscheinlichkeit, dass eine nachfolgende Anfrage einen genau identischen User-Agent: Wert enthält (so dass dieser Client daher den bereits gespeicherten Inhalt erhalten kann), ist sehr gering.

Tatsächlich würde der HTTP-Server sogar große Mengen von UserAgents (die aufgrund seiner Konfiguration als funktional äquivalent angesehen werden) während der Verhandlung identisch behandeln - aber der Vary: Header erlaubt es dem HTTP-Server nicht, dem Caching-Proxy mitzuteilen, welche Teile der UserAgent-Strings vom HTTP-Server während der Verhandlung als signifikante Inhalte bewertet wurden. Der Proxy-Server kann nur erfahren, dass der UserAgent eine Rolle gespielt hat - und sich dessen bewusst, muss der Proxy einzelne UserAgents als verschieden behandeln, selbst wenn der HTTP-Server dies nicht so handhaben würde.

Daher führt die Verwendung von Filterregeln, die den UserAgent HTTP-Header auswerten, dazu, dass jegliches Caching für auf diese Weise erstellte Antwortpakete vollständig deaktiviert wird. Der Benutzer von mod_gzip sollte sich dieser Wirkung absolut bewusst sein - und daher, wenn möglich, andere Filtermethoden verwenden (die eine kleinere Anzahl möglicher unterschiedlicher Werte haben), um die gleiche Art der Differenzierung zwischen diesen HTTP-Clients zu ermöglichen.

Ursprünglicher Standort dieses Dokuments:

http://www.schroepl.net/projekte/mod_gzip/cache.htm

Share: X/Twitter LinkedIn

Erhalte neue Beiträge in deinem Posteingang.

Kein Spam. Jederzeit abmelden.