Skip to content

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 :

Danke für den Artikel. Da hab ich mich seit Jahren nicht mehr beschäftigt. Vielleicht sollte ich das mal wieder auffrischen...

Kommentar schreiben