HTTP: Header und Browsercaching
Die Standardeinstellungen der gängigen Webserver nginx und Apache sind in vielen Linux-Distribution so vorgenommen, dass sie ganz gut mit statischen Dateien umgehen können. Bei dynamisch generierten Inhalten sind sie allerdings grundsätzlich machtlos und man muss etwas in die Trickkiste greifen, um das Browsercaching sauber hinzubekommen.
Statische Inhalte
Bei ordentlich umgesetzten Websites haben statische Inhalte, wie
Bilder, JavaScript, CSS, Schriften und was einem da noch so alles einfallen
könnte, grundsätzlich einen gesetzten Last-Modified-Header auf die
letzte Änderungszeit der jeweiligen Datei und einen gesetzten
Cache-Control-Header mit einem MaxAge sehr weit in der Zukunft liegend. Alternativ kann man auch einen Expires-Header in der Zukunft liegend benutzen.
Wenn einem bei sowas ein Fehler unterlaufen ist, wird einem Google
Pagespeed oder YSlow freundlich aber bestimmt auf die Fehlkonfiguration
hinweisen.
Der Vorteil an diesem Vorgehen ist, dass Browser eine einmal gecachte
statische Datei nie wieder anfragen werden. Die Seitenaufbauzeit
bereits besuchter Seiten und der erzeugte Traffic wird dadurch auf ein
Minimum reduziert.
Dateiänderungen
Es gibt Leute, die den Grund aufführen, dass sie eine JavaScript- oder CSS-Datei gerne immer mal wieder ändern möchten und vermeiden wollen, gegen gefüllte Browsercaches zu laufen. Dieser Grund ist ungültig, wenn man in der Webapplikation grundsätzlich den Ansatz fährt, den Dateinamen der jeweiligen veränderten statischen Datei zu ändern sobald sich der Inhalt geändert hat. Bei JavaScript und CSS gehört es heutzutage aus Performancegründen zum guten Ton, es zu minifizieren. Im gleichen Zug kann man bei Änderungen dem Dateinamen ja auch noch um eine Hashsumme des Inhalts anreichern.
Bei Bildern und anderem gibt es meines Erachtens ebenfalls keine nennenswerten Showstopper. Die Arbeitsprozesse der Webapplikation müssen einfach darauf ausgerichtet sein bzw. werden.
Dynamische Inhalte
Webserver können grundsätzlich keine Standardaussage treffen, wie lange z.B. das HTML eines Blogbeitrags vom Browsercache vorgehalten werden soll, ohne zwischendurch neue Inhalte abzufragen. Das Blogsystem kann das in der Regel auch nicht, weil es nicht vorher wissen kann, wann ein Autor einen neuen Beitrag veröffentlichen wird oder wann eine eingebundene Twitternachricht in der Seitennavigation aufpoppen wird. Es kann aber eine klare Aussage machen, wann der Inhalt einer bestimmten URL das letzte Mal aktualisiert wurde.
Hierfür braucht das System
lediglich den Last-Modified-Header oder einen Etag-Header setzen.
Browser werden bei jedem Aufruf den Last-Modified-Zeitpunkt und / oder
das Etag der gecachten Version mitsenden. Wenn das Blogsystem
feststellt, dass die Version bereits die aktuelle ist, antwortet es mit
einem HTTP 304 und sendet keinen Inhalt. Der Browser weiß damit, dass er
bereits die aktuelle Version hat und kann sie ohne Download des HTML darstellen. Falls es eine neue Version gibt, kommt ein regulärer HTTP 200 mit Inhalt.
Ein MaxAge sollte man bei HTML nur setzen, wenn man sich absolut sicher ist, keine neuen Inhalte in der gegeben Zeit ausliefern zu wollen. Einen Blogbeitrag zu veröffentlichen und dies direkt zu vertwittern könnte bei heißen Browsercaches der jeweiligen aufrufenden Benutzer sonst peinlich werden.
Problem
Die von mir aufgezeigten Funktionsprinzipien des Browsercaches ohne Nachfrage bei statischen und mit Nachfrage
bei dynamischen Inhalten werden von keiner mir bekannten
Standardwebapplikation sauber implementiert. Im Falle von Blogsystemen schaffen es diesbezüglich weder Wordpress noch Serendipity, aus der Standardkonfiguration heraus gute Scores
bei PageSpeed zu bekommen. Sie greifen den grundlegenden Mechanismus des Browsercachings nicht auf.
Die Gründe für derartige Designlücken sind mir nicht bekannt. Offenbar ist Frontendperformance für viele Leute nicht so wichtig. Es kann auch sein, dass wegen der gesteigerten Komplexität von der Umsetzung abgesehen wird.
Für mich persönlich haben Effizienz und Zuverlässigkeit die höchste Priorität. Der Funktionsumfang ist zweitrangig.
Varnish mit synthetischem Last-Modified-Header
Varnish kann man einen Last-Modified-Header zur Backendantwort hinzufügen lassen, um so für die Zeit, in der das jeweilige Objekt im Cache von Varnish liegt, Browsercache mit Nachfrage zu unterstützen.
sub vcl_fetch { if (!beresp.http.last-modified) { set beresp.http.Last-Modified = beresp.http.date; } }
Varnish bügelt damit etwas aus, was in der Applikation vernachlässigt wurde. Probiert es einfach mal aus.
Kommentare
Frank Agerholm am :