Caching in TYPO3 – Teil 2

Das cHash Parameter Geheimnis gelüftet – Cache Varianten einer Seite

|Benni Mack

Jeder möchte dass TYPO3 die Seiten der Website noch schneller lädt. Sobald Du die Konzepte hinter Cache Varianten von TYPO3 Seiten verstanden hast, weißt Du genau, wo Du TYPO3 Websites noch schneller bekommst – und keiner der Redakteure muss sich jemals wieder wegen der Ladezeit im Frontend beschweren. Deshalb: Lass uns in die Geheimnisse des cHash Parameters lüften!

Was wir von Teil 1 noch wissen

Man muss nicht denselben Inhalt einer Seite immer und immer wieder neu generieren lassen. Deshalb hat TYPO3 von Haus aus starke Caching Techniken mit dabei. Im ersten Teil unserer Caching Blog Reihe haben wir bereits von „First Hits“, „Nicht cachebaren Blöcken“, und von „Fully Cached Pages“ gehört.

Häufig ist das Ausliefern einer TYPO3 Seite ein Problem, weil TYPO3 nicht die ganze Bandbreite ausschöpft um eine Seite zu einer „Fully Cached Page“ zu bringen. Der Worst Case ist es, den „no_cache“ Parameter als TypoScript Option oder GET–Parameter zu setzen – dadurch wird TYPO3 angewiesen, gar nicht auf das Caching zurück zu greifen. Besser ist es die „Nicht cachebaren Blöcke“ (USER_INT / COA_INT) zu cachebaren Alternativen zu migrieren. Dadurch wird es TYPO3 möglich, zumindest statische Teile der Seite zu cachen, und sie beim nächsten Aufruf aus dem Cache zu laden.

Plugins aus Extensions cachen

Häufig werden Plugins verwendet, um dynamische Inhalte und komplexe Business–Logik auf einer Website zu platzieren – im Vergleich zu normalen Inhaltselementen wie ein Bild-Element oder eine Aufzählungsliste. Bei Extbase–Plugins kann jede Action als „cachebar“ oder „nicht cachebar“ konfiguriert werden. Für unser Beispiel haben wir eine kleine Extension geschrieben — wir nennen sie „simplenews“.

Das Plugin der Extension hat zwei verschiedene Actions / Views als Ausgabe-Arten. Eine Listen-Ansicht („list“ action) gibt die aktuellsten News aus, und eine Detail-Ansicht („detail“ action) um alle Infos einer News auszugeben. Dies ist ein Parade-Beispiel um etwas klarzustellen. Keine der beiden Ansichten – bei News oder Listenansichten allgemein – wird die „nicht cachebare“ Option benötigt. Schließlich ist eine News für jeden Besucher dieselbe. Aber wie macht man eine Seite dann zu einer „Fully Cached Page“ wenn ein Plugin auf GET–Parameter wie tx_simplenews_list[action]=detail in der URL reagiert? Es sollte ja unterschiedliche Dinge ausgeben, auch wenn es dieselbe TYPO3 Seite ist. Wir bei b13 nennen dies „Varianten“ derselben Seite. Und wenn Actions eines Plugins bereits cachebar sind, funktioniert bereits alles.

Was muss denn eigentlich gecached werden?

TYPO3 speichert den Inhalt einer Seite im Cache basierend auf einigen Faktoren, die ich mal genauer erläutere. Diese Parameter bauen den Cache-Identifier im „pages“ Cache - und eben nicht nur die Seite / URL oder die Seiten-ID.

Name des Parameters

Was macht der Parameter?

id

Seiten ID, der durch das URL Routing und den Slug in einer PSR-15 middleware aufgelöst wurde. Letztendlich ist dies die „uid“ aus der Datenbank-Tabelle „pages“ der Standard-Sprache. Früher war das der „id“ GET–Parameter aus der URL.

type

auch bekannt als „typeNum“ in TypoScript, um verschiedene Ausgabeformate zu definieren. Der Type ist numerisch und super praktisch um neben HTML auch eine Variante der Seite als RSS Feed oder für Headless als JSON auszugeben.

groupIds

Die IDs der aktiven Benutzergruppen des angemeldeten Benutzers. Wenn kein Benutzer angemeldet ist, wird der Wert „0,-1“ verwendet.

MP

Der optionale MountPoint Parameter, welcher den alternativen Kontext der Seite darstellt, wo diese im Seitenbaum verankert wird. Nur aktiv, wenn MountPoints verwendet werden.

site

Der Identifier aus der aktuell verwendeten Site–Konfiguration.

siteBase

Die URL der Site–Konfiguration und der aktuellen Sprache.

staticRouteArguments

Weitere URL Parameter, die aus dem Page Routing aus dem URL-Pfad aufgelöst wurden – zum Beispiel aus Route Enhancern.

dynamicArguments

Weitere relevante GET–Parameter, die an der URL hängen und die „Variante“ für den Cache beeinflussen.

Der interessanteste Teil sind die „dynamicArguments“, die verbunden sind mit einem Konzept namens „cHash“, welches seit TYPO3 v3.8 vorhanden ist. Der Erfinder von TYPO3, Kasper Skårhøj, hatte bereits 2005 einen vielgelesenen Beitrag „The mysteries of cHash“ über das Thema „cHash“ geschrieben. Die Lektüre lohnt sich, wenn Du weiter die Geschichte zur Entstehung eintauchen möchtest.

Das Hauptproblem für unsere Caches

Die URL zu einer Seite mit dem Plugin der simplenews Extension würde so aussehen:

www.example.test/en/my-page mit der Listen–Ansicht und cachebar für standardmäßig 24 Stunden.

Ein Link auf die Detail–Ansicht sieht dementsprechend so aus:

www.example.test/en/my-page?tx_simplenews_list[action]=detail&tx_simplenews_list[news]=23&cHash=ef5f1456ee5a3585b65a69afc9b3c9f8

Der cHash Parameter ist eine Art Signatur, die sicherstellt, dass dieselbe Seite (in dem Fall mit der URL „/my-page“) etwas anderes ausgegeben werden soll, und genau diese Information in einer Cache–Variante abgelegt und wieder neu geladen werden soll.

Aber wozu benötigt man den cHash GET–Parameter überhaupt? Unser obserstes Ziel ist es ja, die Website so schnell wie möglich zu laden – auch wenn es GET–Parameter mit „detail“ etc enthält. Und die möchten wir auch aus dem Cache laden, wenn die Seite erneut besucht wird. Nun ja, dann könnte ich ja auch ein kleines Skript, welches einfach diverse GET–Parameter an die URL hängt, wie in diesem Beispiel:

www.example.com/en/my-page?benni=cool
www.example.com/en/my-page?benni=coolcool
www.example.com/en/my-page?benni=coolcoolcool
www.example.com/en/my-page?benni=coolcoolcoolcool

Das könnten ja genau so weitere „Cache–Varianten“ sein, wer weiß das schon? TYPO3 könnte für jeden der Aufrufe einen separaten Cache-Eintrag anlegen – auch wenn die Seite immer die Listen-Ansicht des Plugins ausgeben würde. TYPO3 wäre dann verwundbar gegen einen sogenannten „Denial-of-Service“ Angriff, weil jedermann die Caches von TYPO3 befüllen könnte, bis der Webserver keinen Speicher mehr hat und die Website dann nicht mehr erreichbar wäre. Das will ja niemand, oder? Ich nicht, du nicht, TYPO3 nicht. Und deshalb braucht es einen Mechanismus um festzustellen, dass die angehängten GET Paramater tatsächlich auch gültig sind, und die URL von TYPO3 generiert wurde. Und genau deshalb hängt TYPO3 automatisch &cHash=aNonReadableString an jegliche URL mit GET–Parametern an.

Der „cHash“ Parameter ist eine Zusammenfassung aller GET–Parameter welche relevant für eine unterschiedliche Ausgabe der Seite sein könnten, welches also letztendlich zu einer Cache–Variante führen können. Diese Zusammenfassung ist dann „hashed“ (Ein-Weg-Verschlüsselung) mit Deinem TYPO3-spezifischen Encryption Key – ein Schlüssel der während der Installation von TYPO3 generiert wird – damit niemand anderes den „cHash“ berechnen kann … außer jemand erlangt Zugriff zum Server.

Links generieren und auflösen mit TYPO3

TYPO3 kümmert sich dabei also um zwei Dinge:

1. Generierung eines Links aus einem Plugin mit eigenen GET–Parametern

Wenn eine URL zu einer Seite mit GET–Parametern gebaut wird, wird eine Signatur mit dem zusätzlichen „cHash“ GET–Parameter angehängt. Damit weiß TYPO3, dass es beim Auflösen der URL auf eine Cache–Variante der Seite zurückgreifen kann.

2. Auflösen von URLs mit zusätzlichen GET–Parametern

Wenn eine Seite mit zusätzlichen GET–Parametern aufgerurfen wird, prüft TYPO3 ob ein „cHash“ Parameter in der URL vorhanden ist, und berechnet die Zusammenfassung der anderen GET–Parameter erneut. Wenn die generierte Prüfsumme dieselbe ist wie der Wert aus der angefragten URL, fragt TYPO3 im Cache nach der Variante der Seite. Falls diese nicht vorhanden ist, oder bereits abgelaufen ist, baut TYPO3 diese Variante und speichert sie im Cache.

Wenn die berechnete Prüfsumme mit dem GET–Parameter nicht übereinstimmt, oder wenn es gar keinen GET–Parameter namens „cHash“ gibt, aber zusätzliche GET–Parameter, gibt TYPO3 eine Fehlerseite zurück. Schließlich gibt es diese Variante der Seite auch nicht, weil es keine gültige Variante der Seite ist. Wird TYPO3 etwas weniger strikt konfiguriert, kann statt einer Fehlerseite auch der ungecachte Content der Seite ausgegeben werden. Ein Cache-Eintrag wird in diesem Fall natürlich auch nicht angelegt, das neugenerieren des Inhalts wird also forciert. Dies wird erreicht, wenn die globale Option „pageNotFoundOnCHashError“ auf false gestellt wird. Dies kann zu erhöhten Ladezeiten führen.

Wir fassen zusammen: Der cHash Mechanismus ist eine geniale Sache weil es TYPO3 erlaubt eine Vielzahl von Varianten einer einzigen Seite im Cache abzulegen und wieder zu laden! Die „no_cache“ Option oder „nicht–cachebare Objekte“ können das Cache–Varianten–Feature nicht nutzen, weil sie für jeden Besucher neu berechnet werden. Aber für eine Listen-Ansicht und eine Detail-Ansicht einer Extension ist „cHash“ wie gemacht.

In der Vergangenheit haben Extensions wie RealURL und CoolURI versucht, den cHash Parameter einer URL in einer Zwischentabelle zu speichern um den Parameter aus der URL zu bekommen—häufig war der Wunsch nach „schönen URLs“ da, oder weil fälschlicherweise der Eindruck bestand, dass dies für Suchmaschinen Nachteile hätte. Letzters ist jedoch ein Trugschluss: Suchmaschinen interessieren sich nicht für schöne GET–Parameter. Zusätzlich war es für TYPO3–Integratoren nie offensichtlich wann die Option „useCacheHash“ in TypoLink (via TypoScript) oder FluidViewHelpern (in Fluid Templates) gesetzt werden sollte.

Wie bekomme ich den cHash Parameter weg?

Die kurze Antwort: Das geht nicht ohne Hacks. Die richtige Antwort: Du möchtest den cHash Mechanismus behalten, um das Caching maximal auszureizen. Mit TYPO3 v9 haben wir allerdings Site–Handling eingeführt, um den cHash nicht als GET–Parameter zu benötigen, weil er aus dem URL Pfad generiert werden kann. Dadurch musst Du dich auch nicht mehr mit URLs die von Leuten als „looks ugly“ bezeichnet werden könnten, herumägern. In früheren Versionen wusste TYPO3 eben nichts über URL Routing und hatte sich nur um GET–Parameter wie index.php?id=13, und die GET–Parameter gekümmert..

Site–Handling von TYPO3 v9, sowie die eingeführten „Slugs“ (also Teile vom URL Pfad) von Seiten lösen also drei Altlasten ab: Die „id“ und „L“ GET–Parameter, und die „sys_domain“ Datensätze, die die Einstiegspunkte in einen Seitenbaum definierten. Die sogenannten „Speaking URLs“ (Sprechende URLs) sind jetzt von Haus aus dabei. Deshalb gibt es nur noch einen einzigen Grund, warum der „cHash“ GET–Parameter jetzt überall bei der URL dabei sein könnte — nämlich weil ein Plugin nun weitere GET–Parameter definiert, die zu einer anderen Variante einer Seite führen soll. Und um es nochmals zu wiederholen: Der einzige Grund, diesen cHash weg zu bekommen ist weil Kunden, Integratoren oder Site Administratoren diese als hässlich bezeichnen. Es gibt natürlich andere Gründe - zum Beispiel wenn man einen Link in einer E-Mail teilen möchte, aber das wäre auch von kosmetischer Natur.

Wenn Du jemals einen Passwort–Zurücksetzen Link bekommen hast, wird es eine hässliche URL sein wie sowas:

accounts.example.test/recovery/?user=123123asduoasd8sdf8sdzf&hash=1n23nxc89xcxsc

Stört es Leute? Nein, sie klicken trotzdem drauf. TYPO3 macht ja dasselbe - solche Links können niemals „schön“ sein, weil es ja „One-Time-Links“ sind – sie werden nur ein Mal benötigt. Niemand muss sich die URL merken und Suchmaschinen sollten sie auch nicht durchsuchen.

Die Route Enhancers kommen zur Hilfe

Ein Link zu einer News-Detail-Ansicht sollte verständlicherweise schöner aussehen oder „sprechender“ sein – und deshalb erlaubt Dir TYPO3 v9’s Site–Handling sogenannte Route Enhancer zu konfigurieren. Route Enhancer verschieben GET–Parameter von Plugins in den „Pfad“ Teil einer URL. Wenn es keine GET–Parameter gibt, benötigt es auch keinen cHash GET–Parameter – auch wenn dieser unter der Haube noch verwendet wrid. Route Enhancer sind verantwortlich dafür, dass diese Parameter eines Plugins beim Generieren eines Links in die URL verschoben verwenden, und auch beim Routing wieder zurück transferriert werden – für beides ist dies in TYPO3 v9 die Page Router Komponente.

Unsere simplenews Extension erstellt in der Listen-Ansicht folgende Links:

example.test/news?tx_simplenews_list%5Baction%5D=detail&tx_simplenews_list%5Bcontroller%5D=Main&tx_simplenews_list%5Bnews%5D=2&cHash=ef5f1456ee5a3585b65a69afc9b3c9f8 

Mit Hilfe eines RouteEnhancers sehen die erstellten Links dann wie folgt aus:

example.test/news/hooray-my-first-news

Dabei wird die kompeltte cHash Funktionalität—und dadurch auch die Cache–Varianten einer Seite—beibehalten!

TYPO3 liefert von Haus aus bereits Route Enhancers mit, die für typische Plugins konfiguriert werden können. Eigene Route Enhancer können aber ebenso geschrieben werden. Um einen Route Enhancer zu aktivieren, muss ein Site Administrator lediglich die Site–Konfiguration bearbeiten und dort konfigurieren. Um also im obigen Beispiel den cHash Parameter nicht mehr in der URL zu haben, beinhaltet unsere sites/main/config.yaml Datei folgenden Abschnitt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
routeEnhancers:
MyNews:
type: Extbase
limitToPages: [13,14,15]
extension: simplenews
plugin: list
routes:
- { _controller: 'Main::list', routePath: '' }
- { _controller: 'Main::detail', routePath: '/{news}' }
aspects:
news:
type: PersistedAliasMapper
tableName: 'tx_simplenews_domain_model_news'
routeFieldName: 'slug'

Auszug aus einer Site–Konfiguration (als yaml Datei)

Der Quellcode dieser Beispiel–Extension ist auch in einem öffentlichen GitHub Repository für Dich einsehbar.

Route Enhancer definieren die verschiedenen Möglichkeiten der GET–Parameter eines Plugins. Ein „Aspect“ definiert die Umwandlung eines GET–Parameters (hier „news“) und wandelt diese als sprechendes Pfad-Segment in die URL um.

Mit dieser Technik weiß TYPO3 also genau, wo die Information für das Pfad-Segment zu finden ist wenn es die URL baut, und gleichzeitig auch wenn die URL aufgerufen wird wie der eigentliche GET–Parameter dann lautet.

OK — das waren jetzt einige tiefe Themen in TYPO3, die wir gestreift haben und wenn wir alles richtig machen, erledigt also TYPO3 bereits die meiste Arbeit für uns!

Es lohnt sich auf alle Fälle, alle Plugins von installierten Extensions (in typo3conf/ext/) zu prüfen, ob diese auch nur “nicht cachebar“ sind wenn es tatsächlich keinen anderen Weg gibt, um so TYPO3 das Caching von Plugins zu erlauben.

GET–Parameter aus Seiten–Varianten ausschließen

Jetzt gibt es natürlich noch Fälle, wo Site Administratoren sich beschweren, dass häufig ein „Seite nicht gefunden“ Fehler auftritt. Der Klassiker hierfür ist, wenn ein Link von Facebook oder von einem Newsletter geteilt wird. In diesen Fällen wird häufig eine Campaign ID oder ein fbclid GET–Parameter hinzugefügt. Wie soll also TYPO3 hier den richtigen cHash für die Cache–Variante wissen können? Die Links wurden ja nicht aus TYPO3 heraus generiert! Und es sollte auch nicht relevant sein, ob eine Campaign ID an der URL hängt, der Inhalt ist ja derselbe.

Meist wird dann die globale Option „pageNotFoundOnCHashError“ deaktiviert, um die cHash Berechnung – und dadurch auch das Caching – in diesen Fällen zu deaktivieren. Aber es ist doch schließlich ein einfacher Fix, der die Probleme behebt! Wenn man genauer hinschaut, gibt es auch dafür in TYPO3 bereits Lösungen!

Alle cHash Optionen zusammengefasst

Pro TYPO3-Installation können globale Optionen gesetzt werden, um z.B. gewisse GET–Parameter komplett aus der cHash Berechnung auszuklammern.

In der LocalConfiguration.php Datei, oder im Settings Modul des TYPO3 Backends gibt es etliche Optionen unterhalb der Ebene $TYPO3_CONF_VARS[FE][cacheHash].

Ich erkläre alle Optionen einmal genauer, damit Du weißt wann welche Option für die cHash Berechnung relevant ist:

Option

Beschreibung

$TYPO3_CONF_VARS[FE][cacheHash][cachedParametersWhiteList] (array)

Diese Option erlaubt es Dir, nur eine gewisse Anzahl von GET–Parameter in die cHash Berechnung einzubeziehen. Das ist besonders sinnvoll bei High Traffic Websites, und wenn Du genau weißt, welche Plugins auf der Seite sind, um die cHash Berechnung schnell und effektiv zu halten.

$TYPO3_CONF_VARS[FE][cacheHash][requireCacheHashPresenceParameters]

Hier kann definiert werden, dass bei gewissen GET–Parametern der cHash zwingend vorhanden sein muss. Wenn kein cHash mitgeliefert wurde, stößt TYPO3 direkt das Verhalten an, welches bei fehlerhaftem oder fehlendem cHash geschehen soll (Caching deaktivieren oder Fehlerseite).

$TYPO3_CONF_VARS[FE][cacheHash][excludedParameters]

Sicherlich die am meist genutzte oder angepasste Option: TYPO3 definiert bereits Parameter (Campaign ID, fbclid) welche niemals in die cHash Berechnung mit einfließen werden sollen – gängige Parameter wie „utm_source“, „gclid“ oder „pk_campaign“ sind auch bereits vordefiniert.

$TYPO3_CONF_VARS[FE][cacheHash][excludedParametersIfEmpty]

Es kann auch tatsächlich in TYPO3–Projekten vorkommen, dass es URLs mit leeren GET–Parametern wie ‘&ftu=’ gibt. Hier kann eine Liste von GET–Parametern angegeben werden, welche nicht als eigene Cache–Variante abgelegt werden sollen, wenn ein Parameter zwar in der URL ist aber ohne einen Wert angegeben wurde. Setze die Option „excludeAllEmptyParameters“ um alle leeren GET–Parameter bei der cHash Berechnung außen vor zu lassen.

$TYPO3_CONF_VARS[FE][cacheHash][excludeAllEmptyParameters] (boolean)

Ein globaler Switch für die obige Option „excludedParametersIfEmpty“.

$TYPO3_CONF_VARS[FE][pageNotFoundOnCHashError]

Die Option hatte ich schon erwähnt und ist standardmäßig aktiv: Wenn eine URL einen cHash benötigt, aber dieser nicht mit den mitgelieferten GET–Parametern übereinstimmt, oder nicht vorhanden wird, zeigt TYPO3 eine Fehlermeldung an – schließlich gibt es die Seitenvariante nicht! Wenn diese Option deaktiviert ist, wird bei Seiten mit fehlendem oder fehlerhaftem cHash das Caching umgangen. Wir empfehlen deshalb, die Option immer aktiv zu haben.

Wir empfehlen, alle Optionen nochmals genau in Deinem System zu prüfen und zu evaluieren, welche Parameter in deinem System verwendet werden und welche Optionen du setzen kannst.

Und nochmals als Wiederholung: Wenn es keinen zwingenden Grund gibt, schaue dass die Option „disableNoCacheParameter“ aktiviert ist, damit Du dein System performanter machst!

Zusammenfassung

Wie wir festgestellt haben, passieren im TYPO3 Frontend etliche Dinge, um das maximale Caching–Verhalten zu ermöglichen. Unser Anliegen ist es ja immer, Websites mit TYPO3 schnell rendern zu lassen – auch wenn sie komplexe Plugins mit Business–Logik auf den Seiten haben. Der cHash Parameter ist also eine gute Sache um Cache–Varianten derselben Seite zu ermöglichen, und wenn Du Site–Handling mit TYPO3 v9 am Laufen hast, hast du diverse Möglichkeiten innerhalb von TYPO3, dies selbst zu konfigurieren – die offizielle Doku liefert auch noch wesentlich mehr Infos.

Jetzt weißt Du wie man Cache–Varianten von Seiten mit cHash korrekt konfiguriert, können wir uns an die nächsten Themen wagen, nämlich um die Seiten-Generierung und die Generierung von Seiten superschnell zu machen. Wenn Du möchtest, dass wir von b13 genauer auf den spezifischen Anwendungsfall deines TYPO3 Projekts schauen sollen, kannst Du uns auch mit einer Performance-Analyse oder konkreten Optimierungs-Änderungen beauftragen!

Finde in Part 1 mehr heraus über uncacheable blocks, „no_cache“-Parameter und weiteren Möglichkeiten das Caching in TYPO3 zu verbessern.