If-Modified-Since nachbauen

PHP unterstützt von Natur aus Anfragen mit If-None-Match oder If-Modified-Since nicht. Doch kann man mit einem kleinen Script diese Funktionalitäten nachstellen, und zwar so, dass der Benutzer keinen Unterschied zwischen der nachgebauten und der server-originalen Funktion feststellen kann.

Die Request-Header "If-None-Match" und "If-Modified-Since" dienen dazu, dass der Client (bzw. Browser) nicht nochmal die ganze Datei, meistens Bilder, vom Server herunterladen muss, wenn sich deren Inhalt im Vergleich zur gecachten Version nicht verändert hat. Die Header dienen also primär dem Cache-Controlling und der Traffic-Reduktion. Wenn die Datei beispielsweise seit der angegebenen Zeit im If-Modified-Since-Header nicht verändert wurde, bekommt der Client eine entsprechende Status-Meldung und kann dadurch die Datei aus seinem Cache sorgenlos verwenden.

<?php
$lastModified = 123456789; // Timestamp der letzten Änderung
$eTag = '"abc-def123-456789"'; // ETag, z.B. md5($lastModified)

// entsprechende Header setzen
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $lastModified).' GMT');
header('ETag: '.$eTag);

// Request-Header überprüfen ..
$httpModSince = !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : '';
$httpModEtag  = !empty($_SERVER['HTTP_IF_NONE_MATCH'])     ? $_SERVER['HTTP_IF_NONE_MATCH']     : '';

// .. ob sie zutreffen
if ($httpModEtag === $eTag || @strtotime($httpModSince) >= $lastModified) {
    // Es gab keine Änderung der Datei
    // Header senden und exit
    header('HTTP/1.1 304 Not Modified', true, 304);
    exit;
}

# Ab hier Ausgabe des Inhalts
# ...
?>

Wenn nun serverseitig keine Änderung des Inhalts festgestellt werden konnte, bekommt der Client den Statuscode "304 Not Modified", damit er weiss, dass er die Datei aus dem Cache nehmen kann. Natürlich wird in diesem Falle auch kein Content mitgesendet.

Die Variable $lastModified enthält die Zeit (Timestamp) des letzten Änderungsdatums der Datei, was beispielsweise mit der Funktion filemtime() herausgefunden werden kann. Die Variable $eTag kann zum Beispiel der MD5-Hash des Inhalts der Datei sein oder auch einfach das letzte Änderungsdatum als Timestamp. Zu beachten ist dabei, dass der Wert des ETags (per Definition) in doppelten Anführungszeichen stehen muss. In den Variablen $httpModSince und $httpModEtag sind die Werte gespeichert, welche der Client an den Server gesendet hat.

Beispiel der Situation: Der Client fordert eine Datei an und bekommt vom Server zusätzlich folgende Antwort-Header:

Last-Modified: Sun, 08 Nov 2009 21:07:55 GMT ETag: "a429e02-5bd3-dc894"

Eine Woche später benötigt der Client die Datei wieder. Er schickt aber noch folgenden Header mit:

If-Modified-Since: Sun, 08 Nov 2009 21:07:55 GMT

Der Server prüft nun, ob die Datei in der Zwischenzeit verändert wurde und stellt fest, dass dies nicht der Fall ist. Er schickt dem Client eine kurze und knappe Antwort:

HTTP/1.1 304 Not Modified

Der Client kann nun die Datei aus seinem Cache verwenden, welche er die Woche zuvor heruntergeladen hat, und spart somit Traffic.

Es sei noch angemerkt, dass die Header "Last-Modified" und "If-Modified-Since" sowie "ETag" und "If-None-Match" jeweils zusammengehören.