Ein Besucher ruft dieselbe Ratgeberseite zum fünften Mal auf, und der Server baut sie jedes Mal vollständig neu zusammen. Dabei hat sich seit Wochen nichts geändert, und der Browser hätte seine Fassung noch im Zwischenspeicher. PHP getlastmod() liefert die Angabe, mit der sich dieser Leerlauf abstellen lässt. Wer die Änderungszeit irgendeiner Datei braucht, greift zu filemtime(). Hier geht es um die eine Datei, die der Besucher gerade angefordert hat, und um das, was sich damit gegenüber dem Browser anfangen lässt.
Zwei Pfeile zwischen Browser und Server: hin die Rückfrage, zurück eine Antwort ohne Rumpf. Genau dieser Kreislauf ist der Kern des Themas.
Was PHP getlastmod() liefert und welche Datei gemeint ist
Der Rückgabewert ist ein Unix-Zeitstempel, also die Zahl der Sekunden seit dem 1. Januar 1970 in UTC. Aus 1789975120 wird mit gmdate('d.m.Y H:i') der 21.09.2026 um 07:18 Uhr; auf einem Server in Europe/Berlin zeigt date() für dieselbe Sekunde 09:18 Uhr an. Eine Zeitzone trägt der Wert selbst nicht, die entscheidet erst die Formatierung.
Wichtiger als das Format ist die Frage, welche Datei gemeint ist. Die Funktion meldet die Änderungszeit der Hauptdatei, also derjenigen, die der Server für diese Anfrage geöffnet hat. Nicht irgendeine Datei, und auch nicht die Datei, in welcher der Aufruf steht. Diese Präzisierung klingt nach Haarspalterei und ist der ganze Unterschied zu filemtime().
Syntax, Parameter und Rückgabewert im Original
Keine Parameter, ein Zahlenwert oder ein false. Kürzer geht eine Signatur kaum:
getlastmod(): int|false
/* no parameters at all
return - unix timestamp of the last
modification of the main script,
false on failure
Gets the time of the last modification of
the current page. The value refers to the
script the server opened for this request,
not to the file the call is written in.
Compare with:
filemtime(string $filename): int|false
which takes a path and uses the stat cache */
Die fehlende Parameterliste ist kein Sparprogramm, sondern die Aussage selbst. PHP getlastmod() bezieht sich immer auf genau eine Datei, und welche das ist, hat der Server schon entschieden, bevor die erste Zeile Code lief. Deshalb gibt es nichts zu übergeben. Der zweite Teil des Rückgabetyps, das false, bekommt weiter unten einen eigenen Abschnitt.
Aus einem Include heraus zeigt der Wert nach oben
Der folgende Versuch macht den Unterschied sichtbar. Eine haupt.php mit der Änderungszeit 1789975120 bindet eine teil.php ein, deren Zeitstempel künstlich auf 1577876400 gesetzt wurde. Beide Dateien geben dasselbe Paar aus:
<?php
/* haupt.php, mtime 1789975120 */
echo getlastmod(); /* 1789975120 */
echo filemtime(__FILE__); /* 1789975120 */
require __DIR__ . '/teil.php';
/* teil.php, mtime 1577876400 */
echo getlastmod(); /* 1789975120 */
echo filemtime(__FILE__); /* 1577876400 */
In der eingebundenen Datei laufen die beiden Werte auseinander, und das ist der Grund, warum eine gemeinsame Fußzeile auf allen Seiten ein passendes Datum anzeigen kann. Sie steht in einem Baustein, meint aber die Seite. Mit filemtime(__FILE__) stünde dort auf jeder Seite dasselbe Datum, nämlich das der Fußzeile.
Der Wert wird beim ersten Aufruf von PHP getlastmod() ermittelt und dann für den Rest der Anfrage festgehalten, nicht mehr nachgeschlagen. Gemessen: Wird die Änderungszeit der Hauptdatei danach von außen auf 1234567890 gesetzt, meldet filemtime(__FILE__) anschließend den neuen Wert, PHP getlastmod() dagegen weiterhin den alten. Auch ein clearstatcache() ändert daran nichts. Zwei Folgerungen: Der Aufruf kostet keinen Zugriff auf das Dateisystem, und der Zwischenspeicher für Dateiinformationen spielt hier keine Rolle. Bei filemtime() muss man den Zwischenspeicher im Blick behalten, hier nicht. Wie er arbeitet und wann er zuschlägt, steht im Tutorial zu fstat() und dem Stat-Cache.
Last-Modified richtig senden
Die Kopfzeile Last-Modified verlangt ein festes Format, und sie verlangt es immer in GMT. Den Zeitstempel liefert PHP getlastmod(), das Format macht gmdate() und nicht date(). Der Unterschied ist kein Schönheitsfehler: Auf einem Server mit der Zeitzone Europe/Berlin liegt date() im Sommer gemessene 7200 Sekunden daneben. Der Browser hält die Seite dann zwei Stunden länger für aktuell, als sie es ist.
<?php
$zeit = getlastmod();
$wert = gmdate('D, d M Y H:i:s', $zeit) . ' GMT';
header('Last-Modified: ' . $wert);
/* richtig:
Mon, 21 Sep 2026 07:18:40 GMT
falsch, mit date() in Europe/Berlin:
Mon, 21 Sep 2026 09:18:40 GMT */
Weitere Kopfzeilen und ihre Reihenfolge behandelt das Tutorial zu HTTP-Headern mit PHP. Für diesen Fall genügt die eine Zeile, und sie muss vor jeder Ausgabe stehen, sonst sind die Kopfzeilen bereits heraus.
If-Modified-Since auswerten und mit 304 Not Modified antworten
Der Server sendet Last-Modified. Beim nächsten Aufruf schickt der Browser genau diesen Wert als If-Modified-Since zurück. Das Skript liest ihn, rechnet ihn mit strtotime() in einen Zeitstempel um und vergleicht ihn mit dem, was PHP getlastmod() meldet. Ist der mitgeschickte Wert nicht älter als der eigene, antwortet der Server mit dem Statuscode 304 Not Modified und beendet sich, ohne einen Rumpf auszugeben.
flowchart TD
A[Anfrage] --> B{If-Modified-Since da?}
B -->|nein| C[Seite bauen]
C --> D[200 mit Last-Modified]
B -->|ja| E{älter als Datei?}
E -->|ja| C
E -->|nein| F[304 ohne Rumpf]
Vollständig sieht die bedingte Antwort so aus. Der Rumpf hinter dem exit wird im Fall 304 nie erreicht, und das ist Absicht: Eine 304-Antwort hat keinen Rumpf. Die Kopfzeile Last-Modified gehört trotzdem mitgesendet, damit die nächste Rückfrage wieder einen Bezugswert hat.
<?php
$zeit = getlastmod();
if ($zeit === false) {
$pfad = $_SERVER['SCRIPT_FILENAME'];
$zeit = filemtime($pfad);
}
$wert = gmdate('D, d M Y H:i:s', $zeit) . ' GMT';
header('Last-Modified: ' . $wert);
$rueck = $_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '';
if ($rueck !== '' && strtotime($rueck) >= $zeit) {
http_response_code(304);
exit;
}
echo 'hier steht der volle Rumpf';
Gegen den eingebauten Entwicklungsserver durchgespielt, ergeben sich drei Fälle: ohne Rückfrage ein 200 mit gesetzter Kopfzeile, mit passender Rückfrage ein 304 Not Modified ohne Inhalt, mit einer um einen Tag älteren Rückfrage wieder ein 200. Genau so soll es sein.
Wo die Prüfung im Skript stehen muss
Die Ersparnis hängt vom Platz ab. PHP getlastmod() kostet nichts, das Rendern der Seite dagegen schon. Wer erst die Datenbank abfragt, die Vorlage baut und dann feststellt, dass ein 304 gereicht hätte, spart die Übertragung und keine einzige Millisekunde Rechenzeit. Die Prüfung gehört deshalb ganz nach oben, vor das erste require der Anwendung.
Als Funktion verpackt, steht sie in einer eigenen Datei und wird als erste Zeile eingebunden. Das exit darin ist kein schlechter Stil, sondern der Sinn der Übung: Es verhindert alles, was danach gekommen wäre. Wie man die einmal geleistete Arbeit zusätzlich auf der Serverseite aufhebt, zeigt das Tutorial zum Datei-Cache mit PHP.
Das jüngste Datum über mehrere Dateien bilden
Eine Antwort besteht selten aus einer Datei. Kopf, Fuß, Konfiguration und ein paar Bausteine kommen dazu, und ändert sich einer davon, ist die Seite neu, obwohl die Hauptdatei unangetastet blieb. Das Maximum aus PHP getlastmod() und den Änderungszeiten der übrigen beteiligten Dateien ist dann der ehrlichere Wert.
<?php
function standDerSeite(array $dateien): int
{
$zeit = getlastmod() ?: 0;
foreach ($dateien as $datei) {
$m = @filemtime($datei);
if ($m !== false && $m > $zeit) {
$zeit = $m;
}
}
return $zeit;
}
$stand = standDerSeite([
__DIR__ . '/inc/kopf.php',
__DIR__ . '/inc/config.php',
]);
Welche Dateien dazugehören, entscheidet der Autor. get_included_files() liefert die Liste zwar zur Laufzeit, aber erst dann, wenn alles eingebunden ist, und damit zu spät für eine Prüfung ganz oben im Skript. Eine kurze feste Liste ist hier die verlässlichere Lösung.
PHP getlastmod(), filemtime() und das Datum aus der Datenbank
Drei Datumsquellen, drei Zuständigkeiten. Die Verwechslung fällt selten sofort auf, weil alle drei ein plausibles Datum liefern.
| Quelle | Meint | Passt für |
getlastmod() | die aufgerufene Hauptdatei, ohne Parameter | Kopfzeile und Fußzeile statischer Seiten |
filemtime() | eine beliebige Datei, die man benennt | Bausteine, Zwischenspeicher, Anhänge |
| Datumsfeld der Datenbank | die letzte Änderung am Inhalt selbst | Artikel, Produkte, Sitemap-Einträge |
Kommt der Inhalt aus einer Datenbank, meldet die Dateizeit den Zeitpunkt der letzten Codeänderung und nicht den der letzten Inhaltsänderung. Für den lastmod-Eintrag einer Sitemap gilt dasselbe, und dort wiegt der Fehler schwerer: Steht dort die Dateizeit, springt nach jedem Ausrollen das gesamte Angebot auf denselben Tag.
<?php
foreach ($artikel as $a) {
printf(
" <url><loc>%s</loc>"
. "<lastmod>%s</lastmod></url>\n",
htmlspecialchars($a['url']),
date('Y-m-d', strtotime($a['geaendert']))
);
}
Wenn der Rückgabewert false lautet
Es gibt genau einen Fall dafür: Es liegt kein Skript als Datei vor. Gemessen kam bool(false) bei php -r und bei Code aus der Standardeingabe, also überall dort, wo der Interpreter den Quelltext nicht aus einer Datei liest.
Die verbreitete Behauptung, die Funktion versage unter der Kommandozeile grundsätzlich, stimmt so nicht. Mit einer echten Skriptdatei lieferte sie unter der SAPI cli denselben Wert wie unter cli-server, im Versuch beide Male 1789975120. Wer auf Nummer sicher gehen will, prüft das Ergebnis und fällt auf filemtime($_SERVER['SCRIPT_FILENAME']) zurück, so wie im Beispiel weiter oben. Eine kleine Diagnosezeile sagt im Zweifel sofort, woran man ist:
<?php
printf(
"sapi=%s last=%s file=%s\n",
PHP_SAPI,
var_export(getlastmod(), true),
var_export(filemtime(__FILE__), true)
);
/* sapi=cli last=1789975120 file=1789975120 */
Was jedes Ausrollen mit dem Datum macht
Jeder Vorgang, der Dateien neu schreibt, setzt deren Änderungszeit neu: ein entpacktes Archiv, eine Übertragung per FTP, ein Abgleich ohne Erhaltung der Zeitstempel. Danach meldet PHP getlastmod() auf jeder Seite denselben Zeitpunkt, und sämtliche Browser verwerfen ihre gespeicherte Fassung gleichzeitig.
Für eine Handvoll Seiten ist das kein Problem. Bei einem großen Angebot bedeutet es, dass eine Korrektur an einer einzigen Vorlage die Wirkung des Zwischenspeichers für alles zunichtemacht. Wer das vermeiden will, nimmt für die Kopfzeile ein inhaltliches Datum und lässt die Dateizeit nur dort stehen, wo der Inhalt wirklich im Code steht. Welche Ausrollverfahren die Zeitstempel erhalten und welche nicht, behandelt das Tutorial zu den Deployment-Strategien.
Fazit
PHP getlastmod() beantwortet eine einzige Frage, dafür ohne Umschweife: Wann wurde zuletzt die Datei geändert, die der Server für diese Anfrage geöffnet hat? Aus einem Include heraus zeigt der Wert nach oben auf die Hauptdatei, und dieser Umstand ist der ganze Unterschied zu filemtime().
Der Nutzen steckt im Kreislauf dahinter. Die Kopfzeile mit gmdate() senden, die Rückfrage aus HTTP_IF_MODIFIED_SINCE lesen, bei Gleichstand mit 304 Not Modified und ohne Rumpf antworten, und die ganze Prüfung so weit oben wie möglich einbauen. Bleibt eine Einschränkung: Kommt der Inhalt aus einer Datenbank, ist die Dateizeit die falsche Quelle. Dann gehört das Inhaltsdatum in die Kopfzeile und in die Sitemap.