Geschrieben von

HTTP-Caching

WebDev

Beim HTTP-Caching handelt es sich um eine Technik, um den Pagespeed einer Website zu verbessern. Vor allem wenn Ressourcen und Elemente auf einer Website eingebunden werden, die auf jeder einzelnen Unterseiten geladen werden müssen, entfaltet HTTP-Caching sein volles Potential. Dazu gleich mehr.

Was ist HTTP-Caching?

Beim HTTP-Caching handelt es sich um ein clientseitiges Caching. Deshalb spricht man beim HTTP-Caching auch von “Browser-Caching”. Caching allgemein bedeutet, dass Daten oder Datenteilmengen temporär gespeichert werden. Bei zukünftigen Anfragen können diese Daten aus dem temporären Speicher schneller zur Verfügung gestellt werden als über den primären Speicherort der Daten.

Funktionsweise von HTTP-Caching einfach erklärt

Heutzutage besitzen alle Browser einen HTTP-Cachespeicher, wo Ressourcen beim ersten Seitenauruf zwischengespeichert werden können, damit diese beim wiederholten Seitenauruf nicht nochmal vom Server angefordert werden müssen. Das beschleunigt die Seitenladegeschwindigkeit. Webentwickler müssen nur sicherstellen, dass die richtigen Angaben für den Browser gemacht werden.

Fordert der Browser beim Server eine HTML-Seite an, werden vom Server verschiedene Angaben im HTTP-Header mitgegeben. Darunter sind auch Caching-Anweisungen zu finden. Ein HTTP-Header kann beispielsweise folgendermaßen aussehen:

200 OK
Content-Length: 1024
Cache-Control: max-age=80
ETag: "a456rrt"

Genau über so einen HTTP-Header wird auch das Caching gesteuert. Bei der Kommunikation zwischen Server und Client können so verschiedene Angaben mitgegeben werden, wie wir gleich sehen werden. Möchte man sich diese Client-Server-Kommnikation über den HTTP-Header ansehen, so kann man das in den Developer-Tools im Network-Tag machen. Dazu wählt man in der Liste die HTML-Seite aus und bekommt rechts unter “Headers” die Informationen (siehe Details weiter unten unter “Testing”).

Header-Steuerung

Möchte man den Header steuern und dabei HTTP-Caching-relevante Einstellungen vornehmen, gibt es verschiedene Möglichkeiten:

  • Über die .htaccess-Datei
  • Über Server-Konfigurationsdateien wie bspw. Apache-Config
  • Programmseitig können Header auch über bspw. PHP hinzugefügt werden
  • Bei CDN-Setups können diese auch meist über die CDN-Einstellungen vorgenommen werden

HTTP-Caching-Angaben

Beim HTTP-Caching unterscheidet man grob in zwei möglichen Angaben, die man vornehmen kann:

  • Caching-Dauer: Hierfür stehen der Cache-Control-Header (cache-control) und der Expires-Header (expires) zur Verfügung. Den Expires-Header gab es unter HTTP/1.0. Ab HTTP/1.1 kommt der Cache-Control-Header zum Einsatz. Beide funktionieren im Kern gleich. Cache-Control kann aber etwas mehr. Unterscheiden tun sie sich auch bei der Angabe. Während beim Cache-Control mit “max-age” die Zeit angegeben wird, wie lange eine Ressourcen gecached werden soll, gibt der Expires-Header das Ablaufdatum als Zeitpunkt in der Zukunft an (z.B. “Mon, 28 Jan 2020 09:00:30 GTM”). Falls beide angegeben werden, bekommt der Cache-Control-Header den Vorrang.
  • Fingerabdruck: Hier stehen das ETag und der Last-Modified-Header zur Verfügung. Beide funktionieren ähnlich. Bis HTTP/1.0 wurde der Last-Modified-Header eingesetzt. Ab HTTP/1.1 ist das ETag fast Standard. ETag stellt einen individuellen Fingerabdruck einer Ressource dar. Wenn sich die Datei ändert, dann ändert sich der ETag mit. Der Last-Modified-Header gab nur den Zeitpunkt an, wann sich die Ressource zuletzt geändert hat. Im Vergleich zum ETag war dies etwas ungenauer.

Caching-Angabe: Cache-Control

Cache-Control wurde mit HTTP/1.1 eingeführt und ersetzt bisherige Angaben wie “Expires”. Mit den Cache-Control-Anweisungen können wir dem Server und Browser mitteilen, wie und wie lange eine Ressource im Cachespeicher abgelegt werden kann. Dadurch können wir bei erneuten Anfragen der gleichen Datei Requests minimieren, um unnötige Datenübertragungen zu vermeiden. In unserem obigen Code-Beispiel ist Cache-Control daran zu erkennen:

Cache-Control: max-age=80

Folgende Angaben sind innerhalb Cache-Control möglich:

  • no-cache und no-store: no-cache gibt an, dass der Browser die im Cache gespeicherte Version nicht direkt verwenden darf, sondern vorher einen Abgleich mit dem Server machen muss. Liegt ein ETag vor, wo Browser und Server vergleichen können, wird ein Download vermieden. no-store ist hingegen strikter: Es verbietet das Setzen einer Datei in den Cache.
  • Öffentlich oder privat: Mit “public” (öffentlich) gibt man an, dass die Ressource von jedem Cache zwischengespeichert werden kann. Mit “private” gibt man an, dass die Ressource nur vom Cache eines einzelnen Nutzers zwischengespeichert werden darf (z.B. Seite mit privaten Informationen).
  • max-age: Gibt an, wie lange die Ressource zwischengespeichert werden kann. “max-age=120” gibt z.B. an, dass die Ressource 120 Sekunden zwischengespeichert werden kann.

Für die einzelnen Files kann man sich an folgende Faustregeln halten:

  • Bei Bildern wie png, jpg, gif, etc. kann ein max-age von einem Jahr gewählt werden, da sich diese Dateien in der Regel kaum ändern
  • CSS-Dateien können sich recht schnell ändern, daher sollte man hier ein max-age von 1 Woche wählen (oder auch 1 Monat; Rücksprache mit Frontend-Team sinnvoll)
  • ico-Dateien (Favicons) ändern sich in der Regel kaum und können auf ein Jahr gesetzt werden
  • JS-Dateien können auf 1 Monat gesetzt werden (außer es wird viel und laufend an JavaScript gearbeitet, dann empfiehlt sich eine kürzere max-age-Dauer)

Cache-Control und Googlebot!
Google ignoriert meistens Angaben im Cache-Control-Header. Grund dafür ist ein “Under Caching” laut Martin Splitt. Zudem würde das Berücksichtigen der Header-Angaben zu mehr Last für den Googlebot führen, was man umgehen möchte. Wer erzielen möchte, dass Google eine Datei neu abruft, sollte mit Namensänderungen (z.B. Änderung der Version im Namen) arbeiten.

Caching-Angabe: ETag

Das obige Code-Beispiel mit “Cache-Control: max-age=80” besagt, dass die Ressource 80 Sekunden im Cache gespeichert werden soll. Jetzt kann man sich fragen?:

  • Was passiert nach den 80 Sekunden?
  • Muss der Browser die Daten erneut herunterladen?

Genau hier kommt das ETag zum Einsatz. Im ETag (steht für Entity-Tag) wird ein Validierungstoken vom Server beim Erstaufruf der Ressource erzeugt und mitgesendet. Dieser Wert ist sozusagen der Fingerabdruck einer Datei. Die ETag-Angabe wird lokal im Cache abgespeichert. Im obigen ersten Code-Beispiel lautet die Angabe ETag: “a456rrt”.

Dieses Tag wird vom Browser benutzt um zu vergleichen, ob sich die lokal abgespeicherte Datei verändert hat oder ob diese noch gleich ist. Sollte die gleiche Ressource vom Browser nochmal angefordert werden, wird dieses Token mitgesendet. Dies geschieht in der Form If-None-Match: “a456rrt”. Auf Serverseite wird das mitgeschickte Token mit dem Aktuellen verglichen. Stimmen die Werte überein, muss die Ressource nicht nochmal geschickt und geladen werden. Durch die identische Zeichenkette weiß der Browser, dass die Ressource nicht geändert wurde. Dadurch wird die Datei lokal abgerufen. Vom Server wird dabei “304 Not Modified” zurückgesendet und nicht 200. 304 ist deshalb so wichtig, da es dem Browser anstoßt, die im Cache liegende Datei erneut abzurufen. Gleichzeitig wird dabei die Angabe im Cache-Control erneuert und wieder auf bspw. 80 Sekunden gesetzt.

Falls sich die Datei geändert hat, würde auf Serverseite ein anderer Token im ETag stehen. Beim Abgleich würde der Browser erkennen, dass diese nicht identisch sind und müsste daher die Ressource neu laden.

Den gesamten Vorgang übernimmt der Browser selbst. Hier muss nur von den Server-Zuständigen sichergestellt werden, dass der Server die Token bereitstellen kann. Beim Apache-Server braucht man Zugriff auf die Apache-Config. Mit dem Eintrag

FileETag All

in der httpd.conf kann man diese Methode aktivieren. Alternativ kann man den Eintrag auch in der .htaccess setzen. Es ist also sehr einfach die ETag-Technologie einzusetzen. Die meisten Webmaster schalten ETag jedoch aus, da die Funktionsweise des ETags mit der Server-Konfiguration nicht harmoniert. Die Hauptfrage lautet dabei, auf welcher Basis man den ETag-Wert erstellt?

Standardmäßig werden folgende Angaben als ETag-Werte in Betracht gezogen:

  • INode: Hierbei handelt es sich um die Dateinummer im System. INode sollte eher nicht als ETag verwendet werden, da bei einem CDN die Nummer einer Datei unterschiedlich sein kann. Grund hierfür ist, dass mehrere Server zum Einsatz kommen, wodurch eigene Nummern generiert werden. Der Browser würde daher die Ressource immer wieder herunterladen, obwohl sie sich vielleicht nicht geändert hat. INode kann also verwendet werden, wenn die Dateien immer nur von einem System ausgeliefert werden.
  • MTime: Hier wird das Datum und die Uhrzeit der letzten Änderung herangezogen. Bei mehreren Servern (z.B. wenn eigene Caching-Server eingesetzt werden) kann das auch problematisch werden, da jeder Server zu einer anderen Zeit die Seite cachen könnte. Dadurch unterscheiden sich wiederum die ETags.
  • Size: Hier wird die Dateigröße als ETag herangezogen. Dies ist auch sehr problematisch. Ändert man im Online-Shop die Preise einiger Produkte, dann ändert sich der Inhalt die Dateigröße bleibt aber die Gleiche.

Solange ein Aufruf der Ressource seitens dem Nutzer innerhalb der “max-age” stattfindet, hätte man hier nichts verloren. Denn: Der ETag wird erst nach Ablauf der max-age-Angabe geprüft. Erst wenn der Nutzer nach der max-age-Angabe die Ressource erneut abruft, würde sich die ETag-Technik in Gang setzen. Im schlimmsten Fall wurden je Datei unterschiedliche ETag-Angaben vom System gesetzt, was dazu führt, dass die Dateien – trotz ETag – trotzdem heruntergeladen werden. Der Browser führt also unnötige Arbeiten aus. Aus diesem Grund wird ETag meistens nicht eingesetzt. Dieser kann dann mit “Header unset ETag” in der .htaccess deaktiviert werden.

Noch eins: Bei statischen Dateien oder Dateien, die sich kaum ändern würde der ETag meistens sowieso keinen Sinn machen. Beispiel: Bilder. In diesem Fall würde der Einsatz des ETags nur unnötigen Aufwand für den Browser bedeuten. Stattdessen ist es besser den max-age-Wert hoch anzusetzen (z.B. für ein Jahr).

Mögliche Probleme beim Caching ohne ETag vorbeugen

Was wenn sich in der Zwischenzeit die gespeicherte Datei geändert hat, man aber kein ETag einsetzt? Z.B. hat sich das Bild für einen Artikel oder es haben sich Angaben im CSS geändert, die für alle Nutzer live sichtbar sein sollten.

Nun ist es so, dass die aktualisierte Datei und Version erst bei folgenden Szenarien selbstständig live verfügbar ist:

  • max-age läuft ab und der Browser muss sich die Ressource vom Server neu holen
  • Der Cache des Nutzers wird gelöscht (z.B. manuelles Löschen)

Es kann also vorkommen, dass Nutzer verschiedene Versionen der Webseite zu sehen bekommen, da einige in der Zwischenzeit ihren Cache löschen und andere wiederum nicht.

Wie bekommen wir es aber trotzdem hin, das Caching beizubehalten und Updates ohne ETag immer sofort sichtbar zu machen?
Wir müssen die URL der aktualisierten Datei ändern. Sobald sich der Dateiname ändert, wird der Browser automatisch dazu gezwungen, die Ressource innerhalb des max-age-Werts neu herunterzuladen. Hierzu ist es sinnvoll sich im Vorfeld bei Dateien wie CSS, JavaScript oder Bildern Gedanken zu einem Dateinamen-Konzept zu machen. In Betracht kommt bspw. der Zusatz zur Versionsnummer oder das Datum der Aktualisierung.

Statt style.css wird dann style.v135.css oder statt script.js wird script.v14.js. Mit diesem Vorgehen ist es auch möglich CSS- und JS-Dateien lange Gültigkeitsdauern im max-age zu definieren, da ein Fingerabdruck im Dateinamen hinzugefügt wird.

Bei Bildern muss man jedoch aus SEO-Sicht aufpassen. Eine geänderte URL führt dazu, dass die alte URL einen 404-Fehlercode schickt. Redirects würden helfen. Jedoch ist das nicht sinnvoll, da dadurch viele Weiterleitungen entstehen (inkl. Weiterleitungsketten). Negative Ranking-Auswirkungen könnten entstehen. Eine Möglichkeit wäre einen Hash ans Ende der URL zu platzieren, gefolgt von Versionsnummern. Google crawlt nicht und ignoriert somit alles hinter dem Hash.

Empfehlung

Als grobe Empfehlung kann man folgendes mitgeben:

  • Die Cache-Control-Angaben sollten immer mitgegeben werden.
  • Das ETag sollte nur dann aktiviert werden, wenn alle Ressourcen vom selben System und vom gleichen Server ausgeliefert werden.
  • Falls unterschiedliche Systeme und Server zum Einsatz kommen, sollte das ETag deaktiviert werden. Stattdessen kann mit Fingerprinting-URLs gearbeitet werden. Alternativ kann man mit dem Last-Modified-Header spielen.

Die Empfehlung sollte aber immer mit der eigenen Infrastruktur abgeglichen und sinnvoll bewertet werden.

Testing

Möchte man den Header einzelner Ressourcen auslesen, wechselt man in die Developer-Console. Im Tab “Network” findet man alle Ressourcen aufgelistet. Klickt nun auf eine Ressource in der Liste, öffnet sich rechts ein Fenster. In diesem Fenster findet man im Tab “Headers” die Response- und Request-Informationen:

HTTP-Header

Die Ressourcen-Liste im Tab “Network” bietet aber auch interessante Informationen. Folgende Spalten sind dazu relevant: Status und Size.

Unter “Size” meldet Chrome bspw. unter anderem, ob die Ressourcen aus dem Cache kommt. Fall ja, ist das dort vermerkt. Dabei sollte man sich nicht von der Spalte “Status” irritieren lassen. Wird die Ressourcen aus dem Cache geladen, meldet Chrome bspw. den Status Code 200. Wenn der Browser aber die Ressource aus dem Cache holt, dann dürfte es keinen Request an der Server geben. Folglich müsste die Spalte “Status” leer sein, da ohne Request kein Status zurückkommen sollte. Davon sollte man sich nicht irritieren lassen. Chrome graut die Spalten z.B. aus. Deshalb sollte man sich hier an der Spalte “Size” orientieren. Bei anderen Browsern kann das jedoch anders aussehen.

Die Spalte “Status” kann auch Aufschluss über das Caching liefern. Ist dort der Status 304 zu sehen, so kann das ein Zeichen sein, dass hier mittels Abgleich mit dem ETag die Ressource aus dem Cache geholt geworden ist. Die Cache-Control-Angabe wurde daher auch aktualisiert.

Um die Aussagen zu validieren, helfen weitere Spalten, die man manuell einblenden muss. Mit Rechtsklick auf die Spalten lassen sich unter “Response Headers” weitere relevante Informationen wie “cache-control” oder das ETag einblenden. Damit lassen sich dann gute Analysen durchführen.

Last modified: 18. Mai 2021