Christian Kruse: Langzeit-Caching: Expires-Header mit Last-Modified oder ohne?

Beitrag lesen

你好 ChrisB,

Sicherlich wird es einen Expires-Header mit einem weit genug in der Zukunft liegenden Datum geben - vielleicht drei oder sechs Monate (man will den Client-Cache ja auch nicht auf ewig monopolistisch besetzen).

Der Expires-Header wird eh nur marginal beachtet. Es gab mal eine Statistik dazu, ich finde sie leider nicht wieder, die besagt hat, dass der Cache nur bei einer kleinen, einstelligen Prozentzahl der Nutzer auch wirklich so genutzt wird, wie es der Betreiber vorgesehen hat. Von daher ist ein weit (3-6 Monate passt da gut) in der Zukunft liegendes Expires absolut unkritisch.

Aber wie sieht es mit einem zusätzlichen Last-Modified-Header aus (Timestamp wäre die mtime der jeweiligen Datei auf dem Server) - „macht das Sinn“, den zusätzlich zum Expires-Header anzugeben?

Auf jedenfall. Ohne Last-Modified kein If-Modified-Since. Und der wird tatsächlich sehr oft erfolgreich benutzt, lt. o.g. Statistik.

Mein Wunschgedanke wäre, dass sich ein Client, der der Expires-Angabe nicht „vertraut“, obwohl er noch eine gültige Kopie der Ressource im Cache hat, damit erst mal zu einem Conditional GET Request zu bewegen wäre, eine If-Modified-Since-Anfrage stellt, und eine 304-Antwort bekommt, die ihn dann doch wieder dazu bewegt, die bereits im Cache vorliegende Version der Ressource anzuzeigen, was immer noch schneller ginge, als wenn er per GET die komplette Ressource, inkl. Response Body, erneut anfordern würde.

Meiner Beobachtung nach gehen die Browser in den Default-Einstellungen wie folgt vor: innerhalb der Session (wenn ich das richtig beobachtet habe dauert die solange an, bis man den Tab schließt oder die Adresse erneut eingibt/durch Enter bestätigt im Chrome, Safari und Firefox; wie lange die Session im IE dauert konnte ich nicht recht herausfinden, allerdings ist sie in jedem Fall zuende wenn man den Browser schließt) wird sich an den Expires-Header gehaltet. Bei einer neuen Session wird der Inhalt erneut abgefragt; gab es einen ETag- oder Last-Modified-Header mit einem conditional GET.

Ist das soweit erst mal durch die Theorie gedeckt? Und hat jemand vielleicht auch Erfahrungen gemacht, ob das in der Praxis so mit den gängigen Clients (Browsern) funktioniert wir vorgestellt?

Siehe oben, im weitesten Sinne stimmt das wohl, ja. Wenn nicht, habe ich mich massiv ver-beobachtet ;-)

Insb. beim IE habe ich da Bedenken, weil dessen Caching-Verhalten, abhängig von mehr oder weniger unsinnigen Auswahlmöglichkeiten für den Nutzer, zumindest in der Vergangenheit ja doch öfter abenteuerliche Resultate hervor brachte.

Auf Einstellungen jenseits der Defaults kann man sich nicht verlassen. Prinzipiell könnte es ja auch sein, dass jemand den Source anpasst um das Caching-Verhalten zu modifizieren. M.E.n. ist das allerdings unbedenklich. Im schlimmsten Fall bekommt der Nutzer mit angepassten Einstellungen halt eine alte Version der Datei vorgesetzt.

Mir fallen da noch Caching-Header mit Werten wie pre-check/post-check ein, die m.W. IE-spezifisch sind - damit könnte man glaube ich zumindest einen IE, der den Caching-Angaben des Servers nicht vertrauen mag, dazu bringen, erst mal die gecachte Version zur Anzeige zu benutzen, und erst anschließend noch mal beim Server nachzufragen, „ist das Ding noch aktuell?“, so dass es das Laden der eigentlichen Seite nicht verzögernd stört.

Ja, IE kennt Cache-Control: post-check=<interval>,pre-check=<interval>. post-check bedeutet: bis zum Ablauf dieses Intervalls immer aus dem Cache liefern. Nach dem Ablauf liefere es aus dem Cache, prüfe aber im Hintergrund nach, ob was neueres da ist. Ist etwas neueres da, wird der Cache geupdated und fortan das neue Objekt ausgeliefert. Das wird so gehandhabt, bis das in pre-check definierte Intervall abgelaufen ist. Nach Ablauf von pre-check ist der Cache veraltet und es wird in jedem Fall neu angefordert.

Dann noch, zurückkehrend zu obigem, wie/worauf setze ich den Expires-Header - modification plus x Monate, oder lieber access/now plus x Monate?
Mir scheint letzteres sinnvoll, denn wie gesagt, die Ressourcen werden sich nie ändern, und deshalb würde ich sie eher ab Zugriff für x Monate cachen lassen.

Ich verwende immer access plus 3 months und fahre gut damit. Beweggrund war der gleiche, warum du die Variante bevorzugst :) Außerdem ist „modification plus 3 months” irgendwann abgelaufen, so dass der Expires-Cache gar nicht mehr zum Zug kommen kann (keine Änderung vorausgesetzt).

Und, zu guter letzt - wenn ich wirklich auf eine If-Modified-Since-Anfrage mit 304 Not Modified antworte, was schicke ich dann (neben dem immer noch gleichen Last-Modified:mtime) für eine Expires-Angabe mit? Auch wieder now + x month, oder lieber gar keine? (Wüsste nicht, warum nicht.)

Hier sende ich wieder access plus 3 months.

再见,
 克里斯蒂安

--
http://wwwtech.de/
<!DOCTYPE html>
<head>
 <meta charset="utf-8">
 <title>Fataler Fehler! – Defunced</title>
 <!--[if lte IE 8]><script src="js/ie.js"></script><![endif]-->
 <link rel="stylesheet" href="http://www.defunced.de/css/screen.css" type="text/css" media="screen, projection">
 <meta name="generator" content="WebIndex/1.0">
 <meta name="description" content="Defunced, die Müllhalde von Christian Kruse">
 <meta name="keywords" content="CK, Christian, Kruse, Müllhalde">
</head>
<body>
  <header>
    <h1>Fataler Fehler!</h1>
  </header>
<p>Ein fataler Fehler ist aufgetreten. Folgende Meldung wurde übermittelt:</p>
<pre>File not found!</pre>
<pre><code>
/var/www/defunced.de/www/lib/modules//default/deliver.rb:33:in deliver' /var/www/defunced.de/www/lib/dispatcher.rb:159:in block in dispatch'
/var/www/defunced.de/www/lib/dispatcher.rb:150:in each' /var/www/defunced.de/www/lib/dispatcher.rb:150:in dispatch'
/var/www/defunced.de/www/lib/webindex.rb:61:in `call'</code></pre>
</body>