Sie sind hier : sebastian1012.bplaced.net/ homepage-neu / kreuz-und-quer / tutorials-info-neuigkeiten-php / http-cache-control-ein-buch-mit-sieben-siegeln.php

HTTP Cache-Control – ein Buch mit sieben Siegeln

Nun dachte ich ja eigentlich, dass ich mich mittlerweile ganz gut auskenne mit dem Browser-Cache und der Kontrolle über selbigen. Damit lässt sich ja eine Menge Last sparen, weil einfach ein 304 Not Modified Header an den Client gesendet wird, wenn der Inhalt im Cache noch aktuell ist. Nun habe habe ich bei einem Projekt mal wieder etwas Neues gelernt…

Und zwar geht es um den Header-Eintrag Cache-Control, wie der Titel bereits vermuten lässt. Bisher habe ich diesen Header-Eintrag so gesetzt:

header("Cache-Control: must-revalidate,public");

Ziel war es, dass der Client immer kurz den Server anfragt (must-revalidate), dabei den HTTP_IF_MODIFIED_SINCE-Header mitsendet und ich so überprüfen kann, ob der Browser-Cache-Inhalt noch aktuell ist. Wenn er noch aktuell ist, sende ich einen 304 Not Modified und beende die Scriptausführung:

$modtime_cache = @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $modtime_page <= $modtime_cache) {   header("HTTP/1.1 304 Not Modified");   exit; }

Das funktionierte auch prima – dachte ich. Ich habe mit Live HTTP Headers überprüft, ob auch 304-Header gesendet werden und alles funktionierte.

Dummerweise gab es aber ein AJAX-Login-Formular. Mit dem konnte man sich einloggen, dann wird der AJAX-Request abgeschickt und bei Erfolg einfach das Loginformular gegen den String „Eingeloggt als xyz“ ausgetauscht. Wenn man nämlich eingeloggt ist, sollte nicht der alte Inhalt aus dem Cache geladen werden, denn bei dem ist ja wieder das leere Loginformular sichtbar (und nicht der String „Eingeloggt als xyz“). Ergo dachte ich mir, dass der Browser ja anfragt, ob der Cacheinhalt noch aktuell ist und ich bei dieser Prüfung einfach noch eine Prüfung einbaue, ob der User eingeloggt ist. Wenn ja, dann sende ich einfach keinen 304. Ungefähr so:

$modtime_cache = @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); if(empty($_SESSION['user_id']) && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $modtime_page <= $modtime_cache) {   header("HTTP/1.1 304 Not Modified");   exit; }

Und jetz kommt der Reinfall:
Ich bin auf der Startseite und logge mich also ein, der String „Eingeloggt als xyz“ erscheint, alles gut. Ich klicke auf das Logo der Webseite (das mit der Startseite verlinkt ist) und schwupps schon kommt das Loginformular wieder. Und das, obwohl die Session noch läuft und ich noch eingeloggt bin. Ich wechsle kurz auf eine andere Unterseite und da erscheint der String „Eingeloggt als xyz“. Zurück zur Startseite und plötzlich ist er auch dort zu sehen.
Ich gucke mir nochmal die HTTP-Header an und sehe, dass da überhaupt nicht „revalidatet“ wird. Der lädt einfach den Inhalt aus dem Browsercache. Ich betone nochmal: TROTZ must-revalidate.

Ich kämpfe mich nun also durch tausend Forenbeiträge, die natürlich alle erreichen wollen, dass der Browsercache überhaupt nicht genutzt wird, damit alles immer schön neu geladen wird. Performancetechnischer Blödsinn, aber diese Leute betreiben meist auch keine großen Projekte. Der Browsercache an sich ist ja eine feine Sache, wenn er tut, was man ihm sagt…und ich wollte ihn weiterhin nutzen, nur im Falle eines eingeloggten Users eben nicht.

Auf meinem Weg stoße ich immer wieder auf max-age=0, must-revalidate. Das soll bewirken, dass der Cache die gespeicherte Datei für 0 Sekunden als aktuell betrachten soll (max-age) und anschließend wieder den Server fragt, ob sie noch aktuell ist. Das klingt erstmal gut, ist es doch genau das, was ich möchte, allerdings hat es bei mir nicht funktioniert.

Dann kam no-cache auf den Plan. Nun würde man denken, dass diese Anweisung vom Namen her den Browser anweist, den Cache für dieses Dokument nicht zu verwenden. Aber Pustekuchen, no-cache bedeutet: „Lade die Datei nie direkt aus dem Cache, sondern frag erstmal beim Server nach“.
Wieso man das dann no-cache nennt, ist mir ein Rätsel…
Edit: Gut, nun lese ich mir gerade meinen eigenen Blogbeitrag zum Thema Client-Caching nochmal durch und da steht bei no-cache genau dieses Verhalten. Man muss wohl erst auf die Nase fallen, bevor man sich so etwas merkt 😉

Meine neue Anweisung für Cache-Control lautet deshalb nun:

header('Cache-Control: no-cache,must-revalidate',true);

Den Unterschied zwischen no-cache und must-revalidate konnte ich leider nicht im Netz ausfindig machen, geschweige denn, weshalb must-revalidate nicht dazu führt, dass eine Validierung stattfindet. Das public habe ich jetzt einfach mal gestrichen, weil ich eh kein SSL und solche Geschichten nutze.
Nun bin ich auf eure Kommentare gespannt. Welche Erfahrungen habt ihr mit Cache-Control? Habt ihr ähnliche Absichten wie ich damit – welche Anweisung nutzt ihr dann?

PS: Habe es jetzt auch endlich geschafft, ein Plugin zu installieren, das die „Smart Quotes“ (öffnende und schließende Anführungszeichen) hier im Blog deaktiviert. Nun kann man also Codes auch direkt kopieren, was früher meist nicht funktionierte.