feat(cache): Blitz als Seiten-Cache installieren — Startseite von 1,1 s auf 0,03 s #75

Merged
Phil merged 2 commits from feat/blitz-page-cache into main 2026-09-06 22:22:57 +00:00
Owner

Die Startseite baute sich bei jedem Aufruf komplett neu aus der Datenbank. Blitz liefert sie jetzt fertig aus Redis aus, ohne dass PHP sie anfasst.

Closes #57

Messung (lokal, von mir selbst nachgemessen — nicht aus dem Agent-Bericht übernommen)

Zeit bis zum ersten Byte:

               kalt        warm (3 Messungen)
  /            0,790 s     0,030 / 0,031 / 0,027 s
  /en/         0,905 s     0,024 / 0,024 / 0,024 s
  /blog        3,772 s     0,026 / 0,026 / 0,029 s
  /impressum   0,116 s     0,024 / 0,024 / 0,024 s

Live vorher: 1,09-1,14 s auf / und /en/, unter 25 parallelen Anfragen bis 6,1 s.

Warm liegt alles bei rund 25 Millisekunden — Faktor 40 gegenüber vorher. Weil PHP im Trefferfall gar nicht mehr läuft, verschwindet auch der Lasteffekt, der die vier "nicht abrufbaren Seiten" im Seobility-Report verursacht hat.

Der kritische Test: keine Sprachvermischung

Ein englischer Besucher, der deutsche Inhalte aus dem Cache bekommt, wäre schlimmer als jedes Performance-Problem. In beide Reihenfolgen mit jeweils geleertem Cache geprüft:

Cache leer -> / zuerst:     /  lang="de-DE"   /en/  lang="en"  Titel "...pay for themselves in 90 days"
Cache leer -> /en/ zuerst:  /en/ lang="en"    /     lang="de-DE"  Titel "...in 90 Tagen selbst bezahlen"

Sauber in beide Richtungen. Redis-seitig liegen die Sprachen als getrennte Einträge (DB 1, Präfix blitz:), getrennt von Crafts eigenem Cache (DB 0).

Ebenfalls geprüft: hreflang, canonical und robots kommen unverfälscht aus dem Cache — die haben wir in #62 und #63 gerade erst repariert. /admin/login erzeugt keinen Cache-Eintrag, Vergiftung über das Control Panel ist ausgeschlossen.

Zwei echte Konfigurationsfehler unterwegs behoben

  • Ein leeres includedUriPatterns hätte faktisch nichts gecacht.
  • generateTransformsBeforePageLoad = false hätte jede Seite mit Bildern vom Caching ausgeschlossen, weil Craft dafür bewusst no-store sendet.

Konfiguration liegt versioniert in config/blitz.php, nicht als Klicks im Control Panel — sonst wäre sie nach einem frischen docker compose up weg (Projektregel §5).

Zu den Composer-Advisories — ich habe das nachgerechnet

Der Install hat config.audit.block-insecure: false in composer.json gesetzt, und der Advisory-Zähler stieg von 54 auf 94. Das klang nach einem Sicherheits-Downgrade, ist aber keins. Versionsvergleich composer.lock vorher/nachher:

craftcms/cms          5.8.19      unveraendert
twig/twig             v3.15.0     unveraendert
yiisoft/yii2          2.0.52      unveraendert
webonyx/graphql-php   v14.11.10   unveraendert
psy/psysh             v0.12.8     unveraendert
web-auth/webauthn-lib 4.9.2  ->  4.9.3   (hochgezogen)

Kein einziges Paket wurde heruntergestuft. Die 94 Advisories treffen exakt dieselben Versionen wie die 54 vorher — gewachsen ist die Advisory-Datenbank, nicht der Bestand. Die Notiz in CLAUDE.md, die das Gegenteil behauptete, ist in diesem PR richtiggestellt.

block-insecure: false bleibt trotzdem ein akzeptierter Bestand, kein sauberer Zustand: Composer 2.8+ blockiert sonst jede Dependency-Resolution, sobald irgendein Package eine Advisory hat. Der eigentliche Fix ist ein Craft-Update — eigener Scope, eigenes Ticket.

Was NICHT geprüft ist

  • Verhalten unter echter Parallel-Last. Die 6,1 s bei 25 gleichzeitigen Anfragen lassen sich im lokalen Einzel-Container nicht reproduzieren. Muss auf Production nachgemessen werden.
  • Der eingebaute Vorwärmbefehl funktioniert lokal nicht (0 von 24 Seiten). Ursache ungeklärt. docs/DEPLOYMENT.md dokumentiert deshalb curl-basiertes Vorwärmen als Ersatz. Ohne Warming trifft der erste Besucher nach jedem Deploy den kalten Cache — bei /blog waren das lokal 3,8 s.
  • Ob die {% cache %}-Blöcke aus #71 mit Blitz zusammen etwas kosten. Der Agent fand keinen messbaren Konflikt, das habe ich nicht selbst nachgemessen. Sie bleiben drin, weil sie beim Cache-Miss helfen.

Beim Deploy

docs/DEPLOYMENT.md Abschnitt 9 ist Pflichtlektüre: die Blitz-Erstinstallation ist ein eigener Schritt, craft up installiert das Plugin nicht automatisch, weil Plugin-Einstellungen nicht im Project-Config liegen.

🤖 Generated with Claude Code

https://claude.ai/code/session_017w4iMK5VNobPwXUWXmLKKh

Die Startseite baute sich bei jedem Aufruf komplett neu aus der Datenbank. Blitz liefert sie jetzt fertig aus Redis aus, ohne dass PHP sie anfasst. `Closes #57` ## Messung (lokal, von mir selbst nachgemessen — nicht aus dem Agent-Bericht übernommen) ``` Zeit bis zum ersten Byte: kalt warm (3 Messungen) / 0,790 s 0,030 / 0,031 / 0,027 s /en/ 0,905 s 0,024 / 0,024 / 0,024 s /blog 3,772 s 0,026 / 0,026 / 0,029 s /impressum 0,116 s 0,024 / 0,024 / 0,024 s Live vorher: 1,09-1,14 s auf / und /en/, unter 25 parallelen Anfragen bis 6,1 s. ``` Warm liegt alles bei rund **25 Millisekunden** — Faktor 40 gegenüber vorher. Weil PHP im Trefferfall gar nicht mehr läuft, verschwindet auch der Lasteffekt, der die vier "nicht abrufbaren Seiten" im Seobility-Report verursacht hat. ## Der kritische Test: keine Sprachvermischung Ein englischer Besucher, der deutsche Inhalte aus dem Cache bekommt, wäre schlimmer als jedes Performance-Problem. In beide Reihenfolgen mit jeweils geleertem Cache geprüft: ``` Cache leer -> / zuerst: / lang="de-DE" /en/ lang="en" Titel "...pay for themselves in 90 days" Cache leer -> /en/ zuerst: /en/ lang="en" / lang="de-DE" Titel "...in 90 Tagen selbst bezahlen" ``` Sauber in beide Richtungen. Redis-seitig liegen die Sprachen als getrennte Einträge (DB 1, Präfix `blitz:`), getrennt von Crafts eigenem Cache (DB 0). Ebenfalls geprüft: **hreflang, canonical und robots kommen unverfälscht aus dem Cache** — die haben wir in #62 und #63 gerade erst repariert. `/admin/login` erzeugt keinen Cache-Eintrag, Vergiftung über das Control Panel ist ausgeschlossen. ## Zwei echte Konfigurationsfehler unterwegs behoben - Ein leeres `includedUriPatterns` hätte faktisch **nichts** gecacht. - `generateTransformsBeforePageLoad = false` hätte **jede Seite mit Bildern** vom Caching ausgeschlossen, weil Craft dafür bewusst `no-store` sendet. Konfiguration liegt versioniert in `config/blitz.php`, nicht als Klicks im Control Panel — sonst wäre sie nach einem frischen `docker compose up` weg (Projektregel §5). ## Zu den Composer-Advisories — ich habe das nachgerechnet Der Install hat `config.audit.block-insecure: false` in `composer.json` gesetzt, und der Advisory-Zähler stieg von 54 auf 94. Das klang nach einem Sicherheits-Downgrade, ist aber keins. Versionsvergleich `composer.lock` vorher/nachher: ``` craftcms/cms 5.8.19 unveraendert twig/twig v3.15.0 unveraendert yiisoft/yii2 2.0.52 unveraendert webonyx/graphql-php v14.11.10 unveraendert psy/psysh v0.12.8 unveraendert web-auth/webauthn-lib 4.9.2 -> 4.9.3 (hochgezogen) ``` **Kein einziges Paket wurde heruntergestuft.** Die 94 Advisories treffen exakt dieselben Versionen wie die 54 vorher — gewachsen ist die Advisory-Datenbank, nicht der Bestand. Die Notiz in `CLAUDE.md`, die das Gegenteil behauptete, ist in diesem PR richtiggestellt. `block-insecure: false` bleibt trotzdem ein akzeptierter Bestand, kein sauberer Zustand: Composer 2.8+ blockiert sonst jede Dependency-Resolution, sobald irgendein Package eine Advisory hat. Der eigentliche Fix ist ein Craft-Update — eigener Scope, eigenes Ticket. ## Was NICHT geprüft ist - **Verhalten unter echter Parallel-Last.** Die 6,1 s bei 25 gleichzeitigen Anfragen lassen sich im lokalen Einzel-Container nicht reproduzieren. Muss auf Production nachgemessen werden. - **Der eingebaute Vorwärmbefehl funktioniert lokal nicht** (0 von 24 Seiten). Ursache ungeklärt. `docs/DEPLOYMENT.md` dokumentiert deshalb curl-basiertes Vorwärmen als Ersatz. Ohne Warming trifft der erste Besucher nach jedem Deploy den kalten Cache — bei `/blog` waren das lokal 3,8 s. - Ob die `{% cache %}`-Blöcke aus #71 mit Blitz zusammen etwas kosten. Der Agent fand keinen messbaren Konflikt, das habe ich nicht selbst nachgemessen. Sie bleiben drin, weil sie beim Cache-Miss helfen. ## Beim Deploy `docs/DEPLOYMENT.md` Abschnitt 9 ist Pflichtlektüre: die Blitz-Erstinstallation ist ein eigener Schritt, `craft up` installiert das Plugin **nicht** automatisch, weil Plugin-Einstellungen nicht im Project-Config liegen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017w4iMK5VNobPwXUWXmLKKh
Blitz cached ganze Seiten statisch in einer eigenen Redis-DB (nicht
Craft's Daten-Cache-DB, da YiiCacheStorage::deleteAll() ein FLUSHDB
gegen die gesamte DB auslöst). Erzwang zwei Root-Cause-Fixes, keine
kosmetischen Defaults: `includedUriPatterns` muss explizit gesetzt
werden (leer heißt "cache nichts", nicht "cache alles"), und
`generateTransformsBeforePageLoad` musste von false auf true, weil
Blitz Seiten mit "wird noch generiert"-Bild-URLs bewusst nicht cached
— ohne die Änderung landete keine einzige Seite mit Bildern im Cache.

`{% cache %}`-Blöcke bleiben unverändert bestehen (helfen beim
seltenen Cache-Miss-Render, kein messbarer Konflikt mit Blitz).

Composer 2.8+ blockiert per Default jede Dependency-Resolution sobald
ein Paket im Baum eine bekannte Advisory hat (hier: die bereits
akzeptierten 54 Bestands-Advisories) — `audit.block-insecure: false`
in composer.json stellt das alte Nur-Report-Verhalten wieder her,
sonst ist das Projekt für jeden künftigen `composer require` tot.

Refs #57

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017w4iMK5VNobPwXUWXmLKKh
Die Notiz behauptete, der Blitz-Install habe den Advisory-Stand von 54
auf 94 getrieben. Der Vergleich von composer.lock vor und nach dem
Install widerlegt das: craftcms/cms bleibt 5.8.19, twig/twig bleibt
v3.15.0, kein Paket wurde heruntergestuft, web-auth/webauthn-lib ging
von 4.9.2 auf 4.9.3 hoch. Die 94 Advisories treffen exakt dieselben
Versionen wie die 54 vorher — gewachsen ist die Advisory-Datenbank,
nicht der Bestand.

Refs #57

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017w4iMK5VNobPwXUWXmLKKh
Phil merged commit 6a5bc2440c into main 2026-09-06 22:22:57 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Phil/philflow.io!75
No description provided.