Sie sind hier : sebastian1012.bplaced.net/ homepage-neu / kreuz-und-quer / tutorials-info-neuigkeiten-php / http-304-not-modified-performancesteigerung-kann-so-einfach-sein.php

HTTP 304 Not Modified – Performancesteigerung kann so einfach sein

Dass die Kommunikation im Internet auf HTTP aufbaut, wissen die meisten Webseiten-Entwickler noch. Für viele ist das Protokoll allerdings eine große Unbekannte, die sehr komplex ist, und die zwar über allem schwebt, was man täglich programmiert, um die man sich aber annähernd nie kümmert kümmern braucht. Und doch lohnt ein Blick in das Protokoll, denn nur wer es kennt, kann alle Möglichkeiten, die HTTP bietet, auch ausnutzen.

Zugegeben, das Hypertext Transfer Protocol ist nicht ganz trivial – die Druckvorschau im Firefox sagt mir, dass die Dokumentation ausgedruckt 177 Seiten umfasst (allerdings ist das bei den meisten Protokollen ähnlich…). Aber ich möchte auch gar nicht HTTP erklären, sondern lediglich auf einen simplen Punkt eingehen, von dem sicherlich die meisten Web-Entwickler schon gehört haben: die HTTP-Statuscodes.

HTTP-Statuscodes kennt fast jeder – zumindest einige davon. Selbst normalen Internetnutzern wird manchmal ein „404 Not Found“ entgegen geworfen, etwa wenn die angeforderte Seite nicht existiert. Natürlich kann Otto-Normal-Surfer damit nichts anfangen, aber das ist eine Usability-Geschichte und soll hier jetzt nicht im Mittelpunkt stehen.
Webentwickler kennen insbesondere folgende Statuscodes, die im Alltag von Bedeutung sind:

Natürlich gibt es noch viel mehr, aber sie sind entweder nur browserintern von Bedeutung (z.B. 1xx) oder nur bestimmte Codes sind wichtig: von 2xx (Request erfolgreich empfangen) kenne ich aus der Praxis eigentlich auch nur 200, 3xx (Redirection) sind insbesondere für SEOs bzw. saubere Umleitungen interessant (vor allem 301), von 4xx (Client Error) sind ein paar mehr von Bedeutung, vor allem 401, 403, 404, 410 und 5xx (Server Error) treten eigentlich nur auf, wenn man gröbere Fehler gemacht hat bzw. gerade der Server nicht erreichbar ist.

Das ist ja alles schön und gut, aber nun zum Thema Performance. Und zwar geht es mir um den Status 304 Not modified.
Hier kurz ein Ablauf, wie ein Dokument oder eine Datei angefragt wird. Client fragt an, Server generiert Dokument, Server schickt es an Client, Client verarbeitet es und zeigt es an. Wie ich ja aber früher schon mal geschrieben habe, kann die Nutzung von Browser-Caches eine Menge Performance bringen – wenn man weiß, wie.

Um eine Performancesteigerung zu erreichen, müsste der Anfrageverlauf so aussehen:

  1. Client fragt Dokument an
  2. falls angefragtes Dokument im Browser-Cache liegt, wird der Zeitstempel desselben mitgesendet
  3. falls Dokument in Browser-Cache liegt, prüft Server, ob seitdem eine Veränderung des angefragten Dokumentes stattfand
  4. fand keine Änderung statt, wird 304 Not Modified zurückgesendet, eine weitere Verarbeitung auf dem Server erfolgt nicht – Client lädt Dokument aus Browser-Cache
  5. fand eine Änderung statt oder liegt das Dokument nicht im Browser-Cache, wird das Dokument neu erstellt und an Client gesendet

Das bedeutet, dass die gesamte Verarbeitung auf dem Server bis auf das Überprüfen der letzten Änderungszeit wegfällt – es ist klar, dass dadurch die Performance erheblich gesteigert wird und dazu noch der Traffic erheblich sinkt, denn statt dem gesamten Antwortdokument muss nun nur noch ein HTTP-Header geschickt werden, mehr nicht. Übrigens gilt das auch für Suchmaschinen-Robots, die ja mitunter sehr hohen Traffic verursachen können. Der Googlebot sendet (meist) das letzte Aktualisierungsdatum mit. Wenn das Dokument nicht geändert wurde, kann somit sehr schnell eine Antwort geschickt (darüber freut sich der Bot) und Traffic gespart werden.

So, wie kommen wir nun dazu?
Zuerst einmal zu Punkt 2, dem Mitsenden des Zeitstempels des angefragten Dokumentes, wenn es sich im Browser-Cache befindet. Dies geschieht automatisch durch die Browser. Nicht alle Browser unterstützen dies, jedoch die meisten. Am Server kommt einfach ein zusätzlicher HTTP-Request-Headereintrag namens HTTP_IF_MODIFIED_SINCE an. Der Zugriff erfolgt demzufolge über $_SERVER[‚HTTP_IF_MODIFIED_SINCE‘].
In dieser Variable steht der Zeitstempel der letzten Dateiänderung, allerdings leider nicht als UNIX-Timestamp (Sekunden seit 01.01.1970 00:00) sondern formatiert nach RFC 2822. Ein Beispiel: Wed, 25 Mar 2009 12:19:53 GMT

Nun haben wir die Zeit der letzten Änderung des Dokumentes aus dem Browser-Cache. Jetzt müssen wir überprüfen, ob das aufgerufene Dokument mittlerweile erneuert wurde. Bei statischen Dateien (die zwecks GZIP o.ä. durch den PHP Parser gejagt werden) funktioniert das per filemtime(). Für die Praxis wichtiger ist aber das Angeben für dynamische Dokumente, denn es interessiert ja meistens nicht, wann die PHP-Datei zuletzt bearbeitet wurde, sondern ob die angezeigte Seite eine Veränderung erfahren hat. Eine Standardlösung gibt es hierbei nicht, stattdessen muss zum Beispiel in einem Online-Shop bei jedem Artikel ein (UNIX-)Zeitstempel mitgeführt werden, der bei jeder Veränderung des Artikels ebenfalls aktualisiert wird. Diesen kann man somit auslesen und weiß, wann die letzte Änderung des Artikels erfolgt ist. Zu einigen Nebenbetrachtungen dazu komme ich weiter unten.

Nun haben wir demzufolge das Datum der letzten Änderung des Dokuments im Browser-Cache und das tatsächliche letzte Änderungsdatum des Dokumentes. Also müssen wir diese nur noch vergleichen. Da das Browser-Cache-Datum aber derzeit noch als RFC 2822 vorliegt, müssen wir es für einen Vergleich erst in einen UNIX-Timestamp umwandeln. Das erledigt die Funktion strtotime().
Nun können die beiden Daten ganz einfach verglichen werden und falls das Datum des Browser-Cache-Dokumentes neuer ist als die letzte, tatsächliche Änderung, bedeutet das, dass sich das Dokument seitdem nicht verändert hat und somit ein 304 Not Modified gesendet werden kann:

$LAST_CHANGE_SERVERSIDE = "1234567890"; // aus Datenbank ermitteln if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $LAST_CHANGE_SERVERSIDE <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {   header("HTTP/1.1 304 Not Modified");   exit; } // Browser-Cache unaktuell oder Dokument nicht enthalten -> neu erstellen echo '<html>...</html>'; ?>

Diese Überprüfung sollte möglichst frühzeitig im Code erledigt werden, alle SQL-Queries, die bei jedem Aufruf der Seite ausgeführt werden müssen, sollten allerdings davor ausgeführt werden, denn durch das exit kommt die Verarbeitung nicht in den nachfolgenden Bereich (das ist ja auch Sinn der Sache).
Übrigens: Wer das exit vergisst, kann sich die Überprüfung ebenfalls sparen, da dann trotzdem der gesamte nachfolgende Code ausgeführt wird (es sei denn, man schreibt den ganzen Rest in den else-Zweig).

Nun noch kurz zu einigen Nebenbetrachtungen. Das letzte Änderungsdatum eines Artikels muss für die Präsentationsseite desselben nicht zwingend auch das letzte Änderungsdatum sein, denn bei komplexen Seiten werden Artikel untereinander verlinkt, es wird der Benutzername des Besuchers angezeigt, wenn dieser eingeloggt ist usw. Das bedeutet, dass das Caching nur dann angewendet werden sollte, wenn man das letzte Änderungsdatum auch kennt. Wenn nicht, dann sollte man lieber das Dokument neu erstellen. Das bedeutet jetzt aber nicht, dass alles hier geschriebene für Seiten mit einem Login oder ähnlichen nicht nützlich ist. Denn dann sollte man die 304-er Prüfung eben nur machen, wenn ein Besucher nicht eingeloggt ist. Allein für den Traffic, den Suchmaschinen-Robots verursachen, entsteht dadurch bereits eine große Entlastung (und der Crawler kann auch mehr Seiten in der gleichen Zeit durchsuchen). Für alle Nebenbedingungen, die eine Seite verändern können, sollte man das 304-Caching nicht anwenden.

Hier noch einige typische Anwendungsfälle:

Nun freue ich mich über eure Wortmeldungen, was ihr darüber denkt. Kennt oder nutzt ihr dieses Verfahren für weitere Anwendungsmöglichkeiten? Wie sind eure Erfahrungen?