Aaron Fischer Ingenieur, Vater, Heimwerker, Problemlöser

09 April, 2007

Der Browser-Cache

Browser & Betriebssysteme

Durch den Relaunch von eventon.de bin ich auf ein problematisches Phänomen gestoßen, das mir zuvor nicht bewusst war: Der Browser-Cache. Die Seite erfuhr ein komplettes Redesign, allerdings blieben die meisten Dateinamen(!) der Bilder und CSS-Dateien gleich. So sah die Seite bei manchen ziemlich komisch aus, als plötzlich ein paar alte Dateien auftauchten, die schon längst durch neue ersetzt wurden.

Ich habe einige Tests angestellt, warum viele sofort die neuen Dateien geladen bekommen und manche noch die alten. Meine erste Vermutung war der LastModified-Zeitstempel der Dateien. Firefox hat eine Option, die das Suchen nach einer neuen Datei auf dem Server komplett abschaltet. Diese ist über das übliche about:config zu erreichen. Wenn der Schlüssel browser.cache.check_doc_frequency den Wert 2 hat, wird nie nach einer neuen Version gefragt. (Den Browsercache selbst kann man über about:cache ansehen). Ich hab mich auch nicht gescheut, mal einen Blick in den Sourcecode von Firefox/Mozilla zu werfen, um nachzusehen wie das denn gehandhabt wird.

Meine Vermutung mit dem LastModified-Zeitstempel war gar nicht so falsch. In der Datei imgCache.cpp wird in der Funktion Get() überprüft, ob das Verfallsdatum der lokalen Datei abgelaufen ist. Wenn dies der Fall ist, wird das Dateialter mit der auf dem Server befindlichen verglichen und wenn dies jünger ist als die lokale, wird die Datei in den Cache aufgenommen (bzw. ersetzt) und das Verfallsdatum erneuert. Also auch keine magischen Tricks (soweit ich das überblickt habe :)), das Problem muss also irgend wo anders liegen.

Da die Seite ziemlich oft besucht wurde, liegen vielleicht (je nach Aktualisierungsintervall) auch auf ISP-Seite noch Reste des alten Layouts im Proxy-Cache, allerdings wird dieser vermutlich viel kürzere Verfallsdaten für die Dateien haben.

Also muss ein reload der kompletten Dateien vom Server (automatisch!) veranlasst werden, und wenn möglich nur ein mal, denn den Browser-Cache komplett abschalten wäre nicht sehr performant. Die Brechstangenmethode wäre folgende:

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".
    gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache"); // für HTTP/1.0```

Einen ähnlichen Effekt lässt sich mit einem Meta-Tag im HEAD-Bereich der HTML-Seite erreichen:

```html
<meta http-equiv="expires" content="0" />

Nun, das würde den Browser-Cache komplett umgehen, aber ihn auch nicht mehr nutzen. Firefox hat standardmäßig 9 Tage Vorhaltefrist für gespeicherte Inhalte eingestellt. Im IE sieht es ähnlich aus. Also wäre eine Möglichkeit, die Header-Einträge für 2 Wochen zu setzen und dann wieder zu entfernen, um so sicherzustellen, das jeder den neuen Content erhalten hat. Doch dies ist nicht sehr Resourcenschonend, eine andere Lösung muss her:

define("VERSION", 2);

// New user, has the current content
if (!isset($_COOKIE["version"])) {
    setVersionCookie(VERSION);
}
// Load new Content once.
if ($_COOKIE["version"] < VERSION) {
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
    header("Last-Modified: ".
        gmdate("D, d M Y H:i:s")." GMT");
    header("Cache-Control: no-cache, must-revalidate");
    header("Pragma: no-cache");

    setVersionCookie(VERSION);
}

function setVersionCookie($v) {
    setcookie("version", $v, time()+(60*60*24*12));
}

Nicht sehr schön, aber funktional.