Langzeit-Caching: Expires-Header mit Last-Modified oder ohne?
ChrisB
- https
0 Der Martin0 hotti0 ChrisB
1 Christian Kruse0 Christian Kruse0 ChrisB
0 ChrisB
Hi,
die „externen Ressourcen“ einer Site möchte ich gerne langfristig im Client-Cache lagern lassen - statisches JavaScript, CSS und auch eine handvoll größerer Hintergrundbilder. (Insb., aber nicht nur, letztere sollen bei Aufruf einer Seite so schnell wie möglich zur Verfügung stehen.)
Die werden so selten geändert werden, dass bei Änderung eine neue „Version“ im Ressourcennamen angegeben werden wird, und die Einbindung in den (ebenfalls statisch gecachten, aber häufigerer Änderung unterworfenen) HTML-Dokumenten angepasst wird.
Jetzt frage ich mich, was bzgl. dieses Vorhabens auf HTTP-Ebene am sinnvollsten ist?
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).
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?
In den RFCs habe ich erst mal nichts gefunden, was dagegen sprechen würde.
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.
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?
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.
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.
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.
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.)
Danke für's Lesen dieser doch eher lang geratenen Fragestellung. (Beim Kontrolllesen merke ich, dass ich wieder mal ordentlich dem Schachtelsatz gehuldigt habe.)
[Und Disclaimer@Hotti: Nein, ich möchte weder eine Broschüre für kleines Geld von deiner Webseite kaufen, noch wissen, wie du irgendwas vergleichbares in konkretem Perl umgesetzt hast. Mich interessiert vor allem der fachliche Aspekt aus HTTP-Sicht.]
MfG ChrisB
Hallo,
die „externen Ressourcen“ einer Site möchte ich gerne langfristig im Client-Cache lagern lassen [...]
Jetzt frage ich mich, was bzgl. dieses Vorhabens auf HTTP-Ebene am sinnvollsten ist?
es überrascht mich, dass gerade du diese Frage stellst, wo du doch eigentlich zu den "alten Hasen" gehörst, die ihr Handwerk verstehen. Denke ich zumindest ...
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).
Keine Sorge. In der Regel haben die Browser eine Obergrenze für den Speicherplatz, den sie für den Cache belegen. Normalerweise fliegt dann irgendwann der älteste oder der am wenigsten verwendete Inhalt wieder raus. Damit löst sich das Problem der Fairness im Cache von selbst.
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?
Finde ich auf jeden Fall sinnvoll.
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.
Das ist eigentlich der Idealfall, den ich bei meinen Browsern auch immer einstelle: Egal, was der Server beim vergangenen Abruf einer Ressource mal als Gültigkeitsdauer angegeben hat - einfach mal fragen gehen. Könnt ja sein ...
Ist das soweit erst mal durch die Theorie gedeckt?
Und durch Praxis. IE, Opera und Firefox benutze ich seit Jahren mit dieser clientseitigen Strategie.
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.
Ja, der ist "von Haus aus" so eingestellt, dass er eine einmal gecachte Ressource bis zum jüngsten Tag immer wieder aus dem Cache holt, ohne überhaupt nachzufragen, ob die überhaupt noch aktuell ist. Das würde deinem aktuellen Vorhaben also volle Kanne entgegenkommen.
Wenn er dagegen vom Nutzer weniger aggressiv eingestellt wird, so dass er doch erst mal mit If-Modified-Since anfragt, passt's ja auch.
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 glaube, das macht für die Praxis keinen Unterschied. Der Browser sieht: Das Ding ist noch ein paar Tage oder Monate gültig.
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.)
Gute Frage, habe ich so noch nicht darüber nachgedacht. Ich würde intuitiv den Expires-Header bei einem 304-Response weglassen, der Client hat die Information ja schon.
Danke für's Lesen dieser doch eher lang geratenen Fragestellung. (Beim Kontrolllesen merke ich, dass ich wieder mal ordentlich dem Schachtelsatz gehuldigt habe.)
Willkommen im Club. ;-)
[Und Disclaimer@Hotti: Nein, ich möchte weder eine Broschüre für kleines Geld von deiner Webseite kaufen, noch wissen, wie du irgendwas vergleichbares in konkretem Perl umgesetzt hast. Mich interessiert vor allem der fachliche Aspekt aus HTTP-Sicht.]
*fg*
Ciao,
Martin
Hi Martin,
es überrascht mich, dass gerade du diese Frage stellst, wo du doch eigentlich zu den "alten Hasen" gehörst, die ihr Handwerk verstehen. Denke ich zumindest ...
Da du mir weitestgehend zustimmst, und auch mal was mit „gute Frage“ kommentierst, verstehe ich diese Überraschung nicht so ganz. Dass ich meine eigenen, für mich logischen Gedanken zu solch einer Thematik gerne noch mal bestätigt bzw. ggf. widerlegt und korrigiert hätte, ist doch wohl nicht so abwegig?
Oder hältst du mich für jemanden, der auch im Schlaf HTTP spricht, und, wenn besoffen, nicht schnarcht, sondern Geräusche wie ein Akustikkoppler macht ...? :-)
MfG ChrisB
Hallo Chris,
es überrascht mich, dass gerade du diese Frage stellst, wo du doch eigentlich zu den "alten Hasen" gehörst, die ihr Handwerk verstehen. Denke ich zumindest ...
Da du mir weitestgehend zustimmst, und auch mal was mit „gute Frage“ kommentierst, verstehe ich diese Überraschung nicht so ganz. Dass ich meine eigenen, für mich logischen Gedanken zu solch einer Thematik gerne noch mal bestätigt bzw. ggf. widerlegt und korrigiert hätte, ist doch wohl nicht so abwegig?
nein, aber mich überrascht, dass du scheinbar unentschlossen vor dich hin sinnierst, obwohl du sonst meist als jemand auffällst, der sich seiner Sache sehr sicher ist.
Oder hältst du mich für jemanden, der auch im Schlaf HTTP spricht, und, wenn besoffen, nicht schnarcht, sondern Geräusche wie ein Akustikkoppler macht ...? :-)
Interessanter Gedanke, aber ... nein, soweit ich mich noch erinnern kann, hast du 2005 noch ganz normal geschnarcht. Viel habe ich davon allerdings nicht gehört, was vermutlich daran liegt, dass ich (angeblich) selbst auch tüchtig gesägt habe.
Dass du -bildlich gesprochen- HTTP im Schlaf kennst, hatte ich aber irgendwie angenommen. Gut, nicht alle Einzelheiten, ist klar ...
Ciao,
Martin
Hi,
noch zum fachlichen :-)
Keine Sorge. In der Regel haben die Browser eine Obergrenze für den Speicherplatz, den sie für den Cache belegen. Normalerweise fliegt dann irgendwann der älteste oder der am wenigsten verwendete Inhalt wieder raus. Damit löst sich das Problem der Fairness im Cache von selbst.
Das ist mir schon bewusst - aber auch hier ziert sich vornehme Bescheidenheit vielleicht eher. Es gibt sicher schon genug Dödel, die ihre Ressourcen mit Expires:Irgendwann-im-Jahr-2035 ausliefern - in deren Ränge will ich mich wenn möglich nicht einreihen.
Das ist eigentlich der Idealfall, den ich bei meinen Browsern auch immer einstelle: Egal, was der Server beim vergangenen Abruf einer Ressource mal als Gültigkeitsdauer angegeben hat - einfach mal fragen gehen. Könnt ja sein ...
Hm, halte ich nur unter Praxis-Gesichtspunkten vielleicht für einen „Idealfall“, weil es genug Leute gibt, die mit ihren Caching-Angaben nicht konsequent sind oder gar Unfug betreiben.
Aber theoretischer Idealfall wäre, dass der Client mich vor Ablauf des Expires-Zeitpunktes gar nicht fragt, wenn er die Ressource noch gecached vorliegen hat. Deshalb auch meine Bedenken, ob sich ein zusätzlich angegebenes Last-Modified nicht vielleicht eher hinderlich auswirkt - weil es beim Client doch „Zweifel“ wecken könnte, ob ich mir mit meiner Expires-Angabe wirklich so sicher war ...
Aber ich merke schon, das werde ich vielleicht am besten irgendwann mal selber in Ruhe ausprobieren, mit einer Reihe von Browsern und Einstellungen und Kontrolle der Logfiles.
Ja, der ist "von Haus aus" so eingestellt, dass er eine einmal gecachte Ressource bis zum jüngsten Tag immer wieder aus dem Cache holt, ohne überhaupt nachzufragen, ob die überhaupt noch aktuell ist. Das würde deinem aktuellen Vorhaben also volle Kanne entgegenkommen.
Ja, ich denke auch. Wie das mit pre-check/post-check genau war, werde ich wohl noch mal irgendwo nachlesen.
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 glaube, das macht für die Praxis keinen Unterschied. Der Browser sieht: Das Ding ist noch ein paar Tage oder Monate gültig.
Ja, aber wenn ich die Möglichkeit habe, ihm bei einer heutigen Anfrage zu sagen, das Ding ist noch x Monate gültig (und ich auch nicht vor habe, innerhalb dieses Zeitraums was an der Ressource zu verändern) - dann erscheint mir das sinnvoller, als zu sagen „noch bis übermorgen“.
Zumal, wenn ich Expires von der mtime der Datei abhängig mache, die ich nie zu ändern beabsichtige - dann habe ich ja nach übermorgen ein Problem.
Ich würde intuitiv den Expires-Header bei einem 304-Response weglassen, der Client hat die Information ja schon.
Ja - aber er hat ihr ja vorher schon nicht geglaubt, sonst hätte er nicht erneut nachgefragt.
Mir erscheint es deshalb sinnvoll, Expires durch erneute Angabe noch mal zu bekräftigen, und (siehe vorheriger Absatz) dabei auch gleich noch mal um x Monate in die Zukunft zu datieren. (Vielleicht ist mein Vertrauen in die Lernfähigkeit von Browsern aber auch einfach maßlos überhöht.)
MfG ChrisB
hi,
[Und Disclaimer@Hotti: Nein, ich möchte weder eine Broschüre für kleines Geld von deiner Webseite kaufen, noch wissen, wie du irgendwas vergleichbares in konkretem Perl umgesetzt hast. Mich interessiert vor allem der fachliche Aspekt aus HTTP-Sicht.]
RFCs lesen kostet nüschd ;-)
Als Praktiker unterstütze ich die Empfehlung der bekanntesten Suchmaschinen und die heißt Last-Modified.
Hotti
PS: In meiner Broschüre gehts nur um die Serialisierung und Strukturierung binärer Daten. Darüber findest Du nichts im Internet. Ob jemand dieses kleine Handbuch kauft oder nicht ist mir egal. Was mich schon zur Genugtuung bewegt sind zu Wagenrädern geformte Mundwinkel von Fachleuten, die diese Technik im produktiven Einsatz erleben.
Hi,
RFCs lesen kostet nüschd ;-)
Habe ich zum Teil getan - ohne aber konkrete Empfehlungen in Hinsicht auf die spezielle Aufgabenstellung zu finden. (Ist auch nicht unbedingt Aufgabe von RFCs.)
Als Praktiker unterstütze ich die Empfehlung der bekanntesten Suchmaschinen und die heißt Last-Modified.
Das von dir wieder sowas absolut pauschales kommen würde, ohne auf den konkreten, ausführlich geschilderten Sachverhalt einzugehen, habe ich mir gedacht; hab's nur nicht mit in den Disclaimer aufgenommen, weil ich mir denken konnte, dass dich das nicht abhalten würde.
PS: In meiner Broschüre gehts nur um die Serialisierung und Strukturierung binärer Daten. Darüber findest Du nichts im Internet. Ob jemand dieses kleine Handbuch kauft oder nicht ist mir egal. Was mich schon zur Genugtuung bewegt sind zu Wagenrädern geformte Mundwinkel von Fachleuten, die diese Technik im produktiven Einsatz erleben.
Ja, Erschrecken und Terror können sich auf verschiedene Weise äußern.
MfG ChrisB
你好 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.
再见,
克里斯蒂安
deliver' /var/www/defunced.de/www/lib/dispatcher.rb:159:in
block in dispatch'each' /var/www/defunced.de/www/lib/dispatcher.rb:150:in
dispatch'Hi,
huch. Sorry für die komische Signatur.
:-) Erinnert an das Namensschild in lateinischen Buchstaben des chinesischen Restaurants in einer englischsprachigen Gegend, das irgendwann mal in Blogs die Runde machte - “Translation Server Error”.
MfG ChrisB
你好 Christian,
danke für deine Antwort; die bestärkt mich darin, das so wie geplant umzusetzen.
Ja, IE kennt Cache-Control: post-check=<interval>,pre-check=<interval>.
Ob die sinnvoll sind, werde ich noch mal überdenken. Nach Beschreibungen im Netz scheint der IE die zumindest stärker zu berücksichtigen als nach deiner zitierten Statistik eine Expires-Angabe; lässt sich also wohl gut als Ergänzung zu einer solchen verwenden, wenn man die Zeitpunkte aufeinander abstimmt.
MfG ChrisB