Sie sind hier : sebastian1012.bplaced.net/ homepage-neu / kreuz-und-quer / tutorials-info-neuigkeiten-php / kein-caching-trotz-gesetztem-expires-header.php

Kein Caching trotz gesetztem Expires-Header

In letzter Zeit gab es recht wenig hier im Blog. Heute ist mir aber mal etwas aufgefallen, worüber ich kurz berichten möchte zum Thema Browser-Caching.

Ich habe ein Baum-Menü auf einer Webseite und lade die Untermenüs der oben liegenden Kategorien per AJAX nach, also am Anfang sieht es so aus:

Wenn man nun über Hauptkategorie 2 fährt, werden die Kindkategorien dieser Kategorie per AJAX geladen und ins Dokument eingebaut (also auf der richtigen Seite, hier natürlich nicht).

Nun ändern sich die Kategorien aber nur recht selten (vielleicht wird 1 mal pro Monat eine neue hinzugefügt oder eine alte entfernt). Deshalb wäre es Quatsch, wenn bei jedem Request tatsächlich in der Datenbank nachgeschaut werden müsste, wie die Unterkategorien denn heißen. Viel eleganter könnte man das über Browser-Caching lösen.

Ich habe also den dafür zuständigen Expires-Header auf 1 Tag in der Zukunft gesetzt:

header("Expires: ".gmdate("D, d M Y H:i:s",time()+86400) . " GMT");

Firefox (andere Browser habe ich jetzt nicht getestet) hat aber trotzdem bei jedem Request alles neu geladen (also HTTP Status 200).

Dann habe ich die Angabe Cache-Control noch hinzugefügt:

header("Cache-Control: public,max-age=86400");

Auch das half beim Firefox nichts.

Nachdem ich dann in meine etwas älteren Scripte (zum Beispiel zum Skalieren von Bildern) geguckt habe, sah ich dort noch 2 Möglichkeiten: ETags und Last-Modified.

ETags wären zwar eine Möglichkeit, aber sind ja nicht mehr ganz zeitgemäß, zumindest wenn ein Projekt auf mehreren Servern läuft.

Dann noch Last-Modified. Damit wird ja angegeben, wann zuletzt eine Änderung an der Datei bzw. am angeforderten Dokument vorgenommen wurde. Dafür habe ich die letzte Änderungszeit der MySQL-Tabelle abgefragt:

$result = mysql_query("SHOW TABLE STATUS LIKE 'kategorien'"); $last_update = $result['Update_time'];

Und diese Zeit habe ich dann als Last-Modified-Header gesetzt:

header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_update) . " GMT");

Für die Funktionsweise des Cachens ist es erstmal gar nicht so wichtig, was im Last-Modified-Header steht, solange es in der Vergangenheit ist. Das Problem entsteht aber, wenn man wirklich mal die Kategorien ändert, dann liefert der Browser eventuell noch die veralteten Informationen aus. Hier muss man abwägen, wie schlimm das wäre, denn einerseits ist die Lösung mit der Update-Time der Tabelle sauberer und flexibler, allerdings kostet sie eben auch bei jedem Kategorien-Aufklappen einen zusätzlichen SQL-Request, damit man im Falle der Unaktualität doch noch die derzeit aktuellen Kategorien laden kann. Um diesen Request zu sparen könnte man also auch einfach Last-Modified: Wed, 01 Jul 2009 05:00:00 GMT nutzen (bzw. ein anderes Datum in der Vergangenheit), sollte dann aber den Expires- bzw. Max-Age-Header nicht zu weit in die Zukunft setzen.

Und schon hat das Browser-Caching funktioniert (304-Header bei wiederholtem Abrufen der Kindkategorien).
Und nun für mich ein kleiner Marksatz wie im Mathelehrbuch:
Merke: Fürs Browser-Caching stets Last-Modified-Header setzen!