HTTP Caching Einmaleins

Letzte Woche mußte ich in einem Kundensystem das Caching optimieren. Damit dieses Wissen nicht wieder verloren geht, habe ich dabei gleich mal die wichtigsten Grundlagen hier zusammengetragen.

Die wichtigsten Tools

Die relevanten HTTP Header


Quelle: HTTP/1.1 Header Field Definition

Expires

  • Erklärt Ablaufdatum der Resource
  • Browser macht keinen Request mehr vor Ablauf dieses Datums

Cache-Control

  • public: Aktiviert Proxy Caching und Caching im Firefox bei SSL Verschlüsselung
  • no-cache: Deaktiviert Caching
  • no-store: Keinerlei Speicherung, noch härter als „no-cache“ (gedacht für Backups etc…)
  • max-age: Ablaufzeit in Sekunden, redundant zu Expires
  • must-revalidate: Immer Server fragen, ob Resource abgelaufen ist
  • (es gibt weitere, die vor allem für Proxies relevant sind)

Last-Modified

  • Erklärt letztes Änderungsdatum
  • Browser schickt einen If-Modified-Since Header beim nächsten Request
  • Server kann ein 403 Not Modified zurückschicken, Browser nutzt Caching Daten

ETag

  • „Entity Tag“
  • Ist eindeutige ID bzw. Hash einer Resource
  • Apache verwendet z.B. dafür Last-Modified-Timestamp, Dateigröße und iNode ID, kann aber beliebig gewählt werden
  • Browser schickt einen If-None-Match Header beim nächsten Request
  • Server kann ein 403 Not Modified zurückschicken, Browser nutzt Caching Daten

Erkenntnisse

  • Expires und Cache-Control: max-age sind gut, um Anzahl der Requests zu drücken
  • Last-Modified und ETag sind gut, um die Datenmenge zu reduzieren
  • Expires und Cache-Control: max-age sind redundant, besser nur eins von beiden verwenden
    • Weniger HTTP Overhead
  • Last-Modified und ETag sind redundant, besser nur eins von beiden
    • Weniger HTTP Overhead
    • Wenn beides verwendet wird, muß auch beides geprüft werden!
  • Optimales Caching: Resource muß nur ein einziges Mal geladen werden, dann keine weiteren Requests mehr
    • Expires in Jahr in die Zukunft setzen
    • Wenn sich Resource ändert, URL ändern (z.B. mit Versionsnummer oder Hash)
    • Das geht nur, wenn das gesamte System (die Webapplikation) darauf ausgelegt ist!

Diskussion

ETags sind in Applikationen etwas leichter zu handhaben als Last-Modified Timestamps, da ein einfacher Stringvergleich ausreicht. Timestamps müssen erst umständlich in UNIX Epochen umgewandelt und verglichen werden, bzw. durch aufwändige Klassen gejagt werden.

Dabei sind sie flexibler zu handhaben – Ein ETag kann aus irgendwelchen Daten bestehen, nötigenfalls aus der Summe mehrerer Metadaten mehrerer Elemente.

Für einfache Szenarios (z.B. Bilder, die von der Applikation serviert werden) reicht als Seed für den ETag ironischerweise meist ein Timestamp einer Datei. Mehrere Seeds erhöhen den I/O oft unnötig, können jedoch im Zweifelsfall jederzeit leicht hinzugefügt werden.

Empfehlung von YSlow und Page Speed ist, immer ein Expires und/oder ein Cache-Control zu senden. Bei Resourcen, die sich jederzeit ändern könnten, fühlt sich das allerdings relativ nutzlos an. Der Selbstversuch bestätigt das erwartete Standardverhalten der Browser, immer beim Server nachzufragen, solange der Server nicht explizit ein Expires gesetzt hat. Von öminösen Heuristiken war nichts zu bemerken. Insofern verbrauchen diese Header bei derartigen Resourcen erst einmal nur Bandbreite.

Dennoch bin ich natürlich an guten Gründen interessiert, Expires und/oder Cache-Control bei Resourcen mit unbekanntem Ablaufdatum einzusetzen!