Redis als Cache-Layer in Statamic: Patterns, die wirklich greifen
Vom Cache-Driver bis zur Tag-basierten Invalidation: wie Sie Redis in Statamic 6 so einsetzen, dass Static Caching schnell bleibt und gleichzeitig sauber invalidiert, sobald Redakteure speichern.
Driver-Wahl
Erst den Cache-Driver, dann die Patterns
Statamic erbt sein Cache-Setup direkt aus Laravel. Das heißt: Alles, was Sie aus Laravel über Cache-Driver kennen, gilt eins zu eins. Bevor wir an Static Caching und Invalidation gehen, stellen wir sicher, dass der Default-Cache-Driver Redis ist - file-Cache skaliert auf mehreren App-Nodes nicht, und database-Cache wirft Ihnen in derselben Tabelle Locks, die Sie gerade vermeiden wollten.
config/cache.php verstehen
In config/cache.php legen Sie fest, welcher Store für Cache::-Aufrufe verwendet wird. Wichtig sind drei Parameter: default (welcher Store), prefix (damit mehrere Apps sich denselben Redis teilen können) und die Connection-Definition in config/database.php unter redis.cache. Den prefix immer explizit setzen - sonst überschreiben Staging und Production gegenseitig denselben Key.
phpredis statt predis
Laravel unterstützt zwei Redis-Clients: phpredis (C-Extension) und predis (Pure-PHP). Phpredis ist drei- bis fünfmal schneller, weil die Serialisierung in C läuft. Setzen Sie REDIS_CLIENT=phpredis und installieren Sie pecl install redis. Predis brauchen Sie nur, wenn die Extension auf Ihrer Plattform nicht verfügbar ist - auf Laravel Cloud und Hetzner-Ubuntu ist sie es immer.
return [
'default' => env('CACHE_STORE', 'redis'),
'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
],
// Eigener Store nur für Statamic Static Caching - auf einer
// separaten Redis-DB, damit Cache::flush() nicht das ganze
// Static Cache wegräumt.
'statamic_static' => [
'driver' => 'redis',
'connection' => 'statamic_static',
'lock_connection' => 'default',
],
],
// Prefix pro Environment, damit Staging und Production sich nicht
// gegenseitig die Keys überschreiben, falls sie denselben Redis nutzen.
'prefix' => env('CACHE_PREFIX', env('APP_NAME', 'app') . '_cache_' . env('APP_ENV', 'production')),
];
// .env (Auszug)
// REDIS_CLIENT=phpredis
// REDIS_HOST=127.0.0.1
// REDIS_PORT=6379
// CACHE_STORE=redis
// REDIS_CACHE_CONNECTION=cache
Static Caching
Statamic Static Caching auf Redis
Statamic kennt drei Caching-Strategien: keine, Half-Measure (Antlers wird gerendert, aber Datenbank-Hits gespart) und Full Measure (kompletter HTML-Response wird gecacht). Half Measure ist der Sweet Spot, wenn Sie personalisierte Bereiche haben - Full Measure für reine Marketing-Seiten. Beide funktionieren mit Redis als Backing-Store.
Half-Measure vs. Full-Measure
Half-Measure speichert das Ergebnis der Statamic-Tags, nicht das fertige HTML - Antlers wird trotzdem gerendert. Full-Measure schreibt den kompletten Response in den Cache und liefert ihn beim zweiten Request, ohne überhaupt PHP zu starten. Auf Marketing-Sites mit zehntausenden Hits pro Tag haben wir mit Full-Measure auf Redis Time-to-First-Byte von 600 ms auf 12 ms gedrückt.
Stash-Tag mit Persistent Cache
Der {{ cache }}-Tag (in Statamic 6 als Stash-Variante verfügbar) cacht Template-Fragmente granular - zum Beispiel eine Navigation oder einen Footer, die seitenweit identisch sind. In Kombination mit einem dedizierten Cache-Store landen die Fragmente in Redis und überleben Deployments. Wichtig: scope=true setzen, wenn der Inhalt vom aktuellen Entry abhängt.
return [
// 'half' lässt Antlers rendern, cached aber Tag-Resultate.
// 'full' cached den kompletten HTML-Response. 'null' = aus.
'strategy' => env('STATAMIC_STATIC_CACHING_STRATEGY', 'half'),
'strategies' => [
'half' => [
'driver' => 'cache',
'expiry' => null, // null = unendlich, wir invalidieren via Hook
],
'full' => [
'driver' => 'application',
'expiry' => null,
'lock_hold_length' => 0,
],
],
// Eigener Cache-Store, damit Cache::flush() das Static Cache
// nicht versehentlich wegräumt.
'cache_store' => 'statamic_static',
// Regeln, wann beim Speichern eines Entries invalidiert wird.
'invalidation' => [
'class' => null, // Default. Erweitern wir gleich per Hook.
'rules' => [
'collections' => [
'pages' => ['urls' => ['/', '/{slug}']],
'tutorials' => ['urls' => ['/de/tutorials', '/de/tutorials/{slug}']],
'glossary' => ['urls' => ['/de/glossar', '/de/glossar/{slug}']],
],
],
],
// Pfade, die niemals gecacht werden (CP, Login, dynamische APIs).
'exclude' => [
'/cp', '/cp/*',
'/!/*',
'/api/*',
],
];
Cache-Setup, das hält?
Wir richten Redis-Cluster, Statamic Static Caching und Invalidation-Hooks für Sie ein - inklusive Last-Test und Runbook.
Invalidation
Caching ohne Invalidation ist nur eine schöne Lüge
Der schwierige Teil am Caching ist nie das Schreiben, sondern das gezielte Wegwerfen. Mit Cache-Tags, Statamic-Events und einer TTL als Sicherheitsnetz bauen Sie ein Setup, das auch nach 18 Monaten Produktion noch frische Inhalte liefert.
Cache-Tags für gruppierte Invalidation
Cache::tags(['glossary', 'nav'])->remember(...) speichert Einträge gruppiert. Beim Speichern eines Glossar-Eintrags reicht Cache::tags('glossary')->flush(), statt alle 4.000 Einzel-Keys einzeln zu löschen. Tags funktionieren in Laravel ausschließlich mit dem Redis- und Memcached-Driver - einer der Hauptgründe, warum wir file-Cache in Production grundsätzlich vermeiden.
Hooks bei EntrySaved und EntryDeleted
Statamic feuert Events bei jeder relevanten Content-Änderung. EntrySaved, EntryDeleted, GlobalSetSaved und TermSaved sind die wichtigsten. Wir hängen Listener daran, die genau die betroffenen Cache-Tags räumen - nicht den ganzen Cache. So bleiben unbeteiligte Seiten warm, und der Cold-Start-Schauer nach jedem Speichern fällt weg.
TTL als Sicherheitsnetz
Selbst mit perfekten Hooks vergessen Sie irgendwann eine Invalidation. Eine moderate TTL (z. B. 24 Stunden auf Tag-Gruppen, 7 Tage auf Static Caching) sorgt dafür, dass spätestens dann Frische zurückkehrt. TTL ist Bequemlichkeit, nicht Strategie - die eigentliche Arbeit machen die Hooks.
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;
use Statamic\Events\EntrySaved;
use Statamic\Events\EntryDeleted;
use Statamic\Events\GlobalSetSaved;
use Statamic\Events\TermSaved;
use Statamic\StaticCaching\Cacher;
class CacheInvalidationServiceProvider extends ServiceProvider
{
public function boot(Cacher $staticCacher): void
{
// Entry geändert → nur die betroffenen Tag-Gruppen räumen.
EntrySaved::listen(function (EntrySaved $event) use ($staticCacher) {
$entry = $event->entry;
$handle = $entry->collection()->handle();
// App-Cache: Tags pro Collection.
Cache::tags([$handle, 'nav'])->flush();
// Statamic Static Cache: nur diese URL invalidieren.
$staticCacher->invalidateUrl($entry->url());
});
// Gelöschter Entry → URL aus Static Cache räumen.
EntryDeleted::listen(function (EntryDeleted $event) use ($staticCacher) {
$url = $event->entry->url();
if ($url) {
$staticCacher->invalidateUrl($url);
}
Cache::tags([$event->entry->collection()->handle()])->flush();
});
// Global-Set (z. B. Navigation, Footer) → Site-weit räumen.
GlobalSetSaved::listen(function () use ($staticCacher) {
Cache::tags(['nav', 'footer', 'globals'])->flush();
$staticCacher->flush();
});
// Taxonomie (z. B. Tags, Categories) → betroffenen Handle räumen.
TermSaved::listen(function (TermSaved $event) {
Cache::tags([$event->term->taxonomy()->handle()])->flush();
});
}
}
Artikel teilen
Hat das geholfen? Wir machen Audit, Workshop oder Coaching auch live
Pakete direkt im Shop. Unser Standard-Tagessatz liegt bei 1.500 € netto.