Tipps & Tutorials

HTTP-Cache-Control: Browser-Caching steuern mit .htaccess


Veröffentlicht am 31.08.2021 von DomainFactory

Wer die Performance seiner Website optimieren möchte, kommt um das Thema Caching nicht herum. Auch wir haben zu diesem Thema schon eine Reihe von Beiträgen veröffentlicht, unter anderem zu Methoden, die Antwortzeiten von Websites zu verkürzen, und zur Steuerung von clientseitigem Caching per HTTP-Header. Dieser Artikel knüpft an den letztgenannten Beitrag an und beschäftigt sich vor allem mit der Nutzung von .htaccess-Dateien zur Caching-Steuerung.

Das Zwischenspeichern von Ressourcen, zum Beispiel im Browser, reduziert die Serverlast und beschleunigt den Seitenaufbau. Die Faustregel für Ihre Cache-Control-Strategie lautet daher: Cachen Sie so viele Ressourcen wie möglich so lange wie möglich und so nah am Benutzer wie möglich – also am besten im Browser.

Cache-Control per HTTP-Kopfzeilen

Zur Erinnerung: Das clientseitige Caching durch Browser-Anwendungen und Proxy-Server (z. B. CDNs) können Sie mithilfe von HTTP-Kopfzeilen steuern, genauer von „Cache-Control“- und „Expires“-Headern von Serverantworten. Damit können Sie beeinflussen, ob und wie lange eine von Ihrem Server ausgelieferte Ressource zwischengespeichert und für weitere Anfragen verwendet werden darf.

Cache-Control: public erlaubt explizit das Cachen in beliebigen Caches, private nur in Browser-Caches. Die meisten Responses dürfen standardmäßig aber auch so zwischengespeichert werden, es sei denn, die Serverantwort enthält im Cache-Control-Header die Anweisungen no-store (Caching nicht gewünscht) oder no-cache (Revalidierung gewünscht) bzw. must-revalidate (Revalidierung, wenn abgelaufen). Allerdings speichert beispielsweise Mozilla Firefox unabhängig davon alle HTTP-Ressourcen mindestens im Memory-Cache, um Funktionen wie Navigieren oder Quelltext-Anzeige ohne weitere Serveranfragen zu ermöglichen.

Um dennoch keine veralteten Inhalte anzuzeigen, werten Caches die Haltbarkeit oder „Freshness“ gespeicherter Ressourcen aus. Diese kann durch Angabe einer konkreten Ablaufzeit oder Ablaufdauer gesteuert werden. Dazu dienen vor allem die „max-age“-Anweisungen im Cache-Control-Header sowie der Expires-Header. Mehr Infos zu Cache-Control finden Sie auf mozilla.org.

Browser-Caching steuern mit htaccess

Cache-Control-Anweisungen werden im Apache mittels der Direktive Header definiert. Zusätzlich kann Apache den „max-age“-Wert auch anhand der Zeitangaben in ExpireDefault oder ExpireByType-Direktiven berechnen. Alle diese Direktiven sind nicht nur in der zentralen Apache-Konfigurationsdatei (meist httpd.conf) verwendbar, sondern auch in .htaccess-Dateien. Voraussetzung ist, dass in der zentralen Konfiguration per AllowOverride die dezentrale Nutzung solcher Direktiven erlaubt wurde (AllowOverride FileInfo für Header, Indexes für Expires oder natürlich All). Das ist insbesondere hilfreich, wenn Sie keinen Zugriff auf httpd.conf haben (etwa in Shared-Hosting-Umgebungen). Ansonsten sollten Sie aus Performance-Gründen die zentrale Konfiguration bevorzugen.

Verschiedene Dateien bzw. Dateitypen müssen beim Cachen unterschiedlich behandelt werden. So können Grafik-, CSS- und JavaScript-Dateien in der Regel aggressiver gecacht werden als Texte oder Preislisten, die häufiger geändert werden. Eine zentrale, dateitypabhängige Cache-Control-Strategie sollten Sie in einer .htaccess-Datei im Wurzelverzeichnis Ihrer Website oder besser gleich in der httpd.conf umsetzen. Sie können dafür zum Beispiel die Direktive <FilesMatch> nutzen:

 

<IfModule mod_headers.c>
# Caching
# 1 Jahr (der maximale unterstützte Wert) für Grafik-/Multimediadateien
<FilesMatch "\.(ico|jpg|jpeg|gif|png|pdf|mp3|mp4)$">
Header set Cache-Control "public, max-age=31536000, no-transform"
</FilesMatch>
# 2 Stunden für Textdateien
<FilesMatch "\.(html|htm|xml|txt|xsl)$">
Header set Cache-Control "public, max-age=7200, must-revalidate"
</FilesMatch>
</IfModule> 

 

Expire-Header gehen auch

Mögen Sie es übersichtlicher, können Sie in Ihrer .htaccess auch Expires-Header festlegen. Expires-Angaben können aktiviert bzw. deaktiviert (Direktive ExpiresActive) sowie allgemein (ExpiresDefault) oder abhängig vom Dokumenttyp (ExpiresByType) spezifiziert werden.

Grundsätzlich haben zwar Cache-Control-Header mit max-age oder s-maxage Priorität vor Expires-Headern. Bei der Berechnung der Gültigkeitsdauer einer Ressource im Cache sucht der Browser also in den Kopfzeilen der zugehörigen Serverantwort zunächst nach einer Angabe für max-age und erst dann nach einer Expires-Angabe. Fehlt beides, bestimmt er die Gültigkeit heuristisch nach Last-modified-Wert, Dateigröße, Nutzungsfrequenz u. ä.

Das ist aber kein Problem, denn Apache generiert bei Expire-Headern automatisch auch gleich einen Header Cache-Control: max-age=X mit der spezifizierten Lebensdauer. Ihr .htaccess-Code kann also beispielsweise so aussehen:

 

<IfModule mod_expires.c>
# Expires-Header aktivieren
  ExpiresActive On
# Typunabhängige Standard-Ablaufzeit setzen
  ExpiresDefault "access plus 1 month"
# Typabhängige Ablaufzeit für JPEG-Dateien (jpeg, jpg, jpe)
  ExpiresByType image/jpeg "access plus 1 year"
# Weitere Dateitypen...
</IfModule>

 

Etwas kürzer gefällig? Statt "access plus 1 year" ist auch die Schreibweise "A31536000" (A plus Zeit in Sekunden) möglich.

Benötigen Sie allerdings noch andere Cache-Control-Werte wie must-revalidate oder no-transform (= Cache darf Datei nicht verändern, etwa Bilder in geringerer Auflösung speichern), müssten Sie diese zusätzlich spezifizieren. Verschachtelte <IfModule>-Anweisungen sind zwar möglich, aber machen Ihren Code wieder komplexer.

Browser-Caching verfeinern

Wollen Sie eigene Header nur für einzelne Verzeichnisse inkl. Unterverzeichnisse setzen, sollten Sie das mit <Directory>-Direktiven in httpd.conf bewerkstelligen. Ist das nicht möglich, legen Sie einfach eine eigene .htaccess-Datei in das betreffende Verzeichnis und konfigurieren dort die gewünschten Werte. Für einzelne Dateien darin nutzen Sie wieder FilesMatch- oder <Files>-Direktiven:

 

<Files "private.html">
# Nicht in geteilten Caches (CDN) speichern 
Header set Cache-Control "max-age=300, private"
</Files>

 

Sie haben das Caching zentral aktiviert und möchten es für bestimmte Ressourcen oder Verzeichnisse per .htaccess wieder abschalten? Leider wird für das Deaktivieren des clientseitigen Cachings häufig noch Folgendes empfohlen (auch auf den deutschen Developer-Seiten auf mozilla.org): Cache-Control: no-cache, no-store, must-revalidate. Aber diese Anweisungen sind zum Teil widersprüchlich. Das aus Kompatibilitätsgründen ebenfalls gern empfohlene zusätzliche Pragma: no-cache ist für Response-Header gar nicht spezifiziert und zudem heutzutage überflüssig, wenn ein Cache-Control-Header vorhanden ist. Letzteres gilt auch für Expires: 0, das nichts anderes sagt als max-age=0.

Besser ist daher einfach Cache-Control: no-store, max-age=0 (das empfiehlt auch die US-Version der genannten Mozilla-Seite): Während no-store das Cachen neuer Ressourcen verbietet, veranlasst max-age=0 den Cache zur Revalidierung schon gespeicherter Dateien. Das folgende Beispiel deaktiviert Caching für PDFs (im Verzeichnis der .htaccess):

 

# DISABLE CACHING
<IfModule mod_headers.c>
  <FilesMatch "\.pdf$">
      Header set Cache-Control "no-store, max-age=0"
  </FilesMatch>
</IfModule>

 

Wenn es nicht auf Anhieb klappt

Ein Hinweis zum Schluss: Bitte beachten Sie auch, dass der tatsächliche Effekt Ihrer Header-Anweisungen in .htacess variieren kann. Gibt es an unterschiedlichen Stellen verschiedene bereichs- oder bedingungsabhängige Anweisungen wie <Directory>, <Files> oder <If>, werden diese in einer bestimmten Reihenfolge ausgewertet, für die zuständigen Module jeweils zu einer aktuellen Konfiguration zusammengeführt und erst am Schluss ausgeführt (ausführlicher hier). Zuerst werden <Directory>-Container und .htaccess-Dateien eingelesen, wobei .htaccess-Anweisungen die im <Directory>-Container überschreiben, wenn das für die entsprechenden Direktiven erlaubt ist. <Files>-Anweisungen werden später gelesen und haben daher (zumindest für mod_headers) Vorrang vor konfligierenden <Directory>- und .htaccess-Anweisungen. Bedingungen in <If>-Anweisungen werden zum Schluss berücksichtigt. So kann es durchaus passieren, dass zum Beispiel eine bestimmte <Files>- oder <If>-Anweisung in httpd.conf die Ausführung Ihrer Anweisungen in .htaccess verhindert und Sie andere Möglichkeiten ausprobieren müssen, um zum Ziel zu gelangen.

Titelmotiv: Photo by Glenn Carstens-Peters on Unsplashhttps://unsplash.com/photos/ky0ljKGar78https://unsplash.com/photos/VhEszoHs07Qhttps://unsplash.com/@glenncarstenspeters

 

Der Autor:


Als Qualitätsanbieter überzeugen wir mit HighEnd-Technologie und umfassenden Serviceleistungen. Mit mehr als 1,3 Millionen verwalteten Domainnamen gehören wir zu den größten Webhosting-Unternehmen im deutschsprachigen Raum.