Targeted static and half-measure cache invalidation for Statamic sites built with a pagebuilder.
Instead of flushing the entire cache on every save, this addon builds a block-index of your pages and invalidates only the URLs that reference the changed content — by entry, global, navigation, or taxonomy term.
| Dependency | Version |
|---|---|
| PHP | ^8.4 |
| Laravel | ^12.0 || ^13.0 |
| Statamic | ^6.0 |
Add the GitHub VCS source to your project's composer.json:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/roxdigital/cache-invalidation"
}
]
}composer require roxdigital/cache-invalidation
php artisan vendor:publish --tag=cache-invalidation-configIn config/statamic/static_caching.php, point the invalidation class at the addon:
'invalidation' => [
'class' => \RoxDigital\CacheInvalidation\ContentDependencyInvalidator::class,
'rules' => [],
],Note: This addon fully replaces Statamic's rule-based invalidator. Keep
rulesas an empty array.
Use a Composer path repository to work against a local clone:
{
"repositories": [
{
"type": "path",
"url": "addons/roxdigital/cache-invalidation"
}
]
}composer require roxdigital/cache-invalidation:@devEdit config/cache_invalidation.php after publishing.
return [
/*
| Collections whose entries have a pagebuilder replicator field.
| Only pages from these collections are indexed.
*/
'pagebuilder_collections' => ['pages'],
/*
| Globals that flush the entire static cache on save.
| Use for globals rendered in shared layout (nav, SEO, redirects).
*/
'globals_flush_all' => ['redirects'],
/*
| Navigations that flush the entire static cache on save.
*/
'navs_flush_all' => ['main_nav'],
/*
| Globals that invalidate pages containing specific pagebuilder block types.
| Format: 'global_handle' => ['block_type', ...]
*/
'global_target_blocks' => [
'reviews' => ['statistics_block'],
],
/*
| Globals that always invalidate explicit URLs.
| Format: 'global_handle' => ['/url', ...]
*/
'global_urls' => [
'settings' => ['/'],
],
/*
| Rules for collection entries.
|
| Use 'all' to invalidate every cached URL when any entry in the
| collection is saved.
|
| Rule without field: invalidate every page containing this block type.
| Rule with field: invalidate pages where the field references this entry.
|
| Format: 'collection_handle' => [['block' => 'type', 'field' => 'handle'], ...]
*/
'collection_entry_rules' => [
'reusable_blocks' => [
['block' => 'reusable_block', 'field' => 'entry'],
],
'projects' => [
['block' => 'project_grid', 'field' => 'projects'],
],
'articles' => [
['block' => 'article_carousel_block'],
],
],
/*
| Collections that always invalidate explicit URLs (e.g. overview pages).
| Format: 'collection_handle' => ['/url', ...]
*/
'collection_urls' => [
'articles' => ['/blog'],
],
/*
| Taxonomies that invalidate pages containing specific block types.
| Format: 'taxonomy_handle' => ['block_type', ...]
*/
'taxonomy_target_blocks' => [
'tags' => ['article_overview_block'],
],
/*
| Taxonomies that always invalidate explicit URLs.
| Format: 'taxonomy_handle' => ['/url', ...]
*/
'taxonomy_urls' => [
'tags' => ['/blog'],
],
];When any content is saved, the addon resolves which cached URLs to clear:
| Trigger | Behaviour |
|---|---|
Global in globals_flush_all |
Flush entire cache + clear block index |
Nav in navs_flush_all |
Flush entire cache + clear block index |
Global with global_target_blocks rule |
Invalidate pages containing those block types |
Global with global_urls rule |
Invalidate the configured URLs |
Entry in collection_entry_rules — 'all' |
Invalidate every currently-cached URL |
Entry in collection_entry_rules — block rules |
Invalidate pages that match via the block index |
Entry in collection_urls |
Invalidate the configured URLs |
| Entry — own URL | Always included |
| Taxonomy term | Same pattern as globals (block targets + explicit URLs) |
The addon maintains a url → blocks[] index in your Laravel cache. Each block is stored as a slim record containing only its type and the fields referenced in your rules — rich text, images, and other large field values are discarded at build time to keep the index small.
The index is built on first access and stored forever. It is cleared when:
- The full cache is flushed (global/nav flush-all).
- Any entry in
pagebuilder_collectionsis saved (page layout may have changed). - Any entry whose collection has a
reusable_blockrule is saved (embedded content may have changed).
Blocks of type reusable_block are expanded inline when the index is built, so pages that embed a reusable block are correctly invalidated when that block entry is updated.
Circular references are detected and skipped.
Override customEntryUrls() in a site-specific subclass for collections that cannot be expressed with config-driven rules:
class SiteInvalidator extends \RoxDigital\CacheInvalidation\ContentDependencyInvalidator
{
protected function customEntryUrls(\Statamic\Entries\Entry $entry): \Illuminate\Support\Collection
{
if ($entry->collectionHandle() === 'team') {
return collect(['/about']);
}
return collect();
}
}Register your subclass in config/statamic/static_caching.php:
'invalidation' => [
'class' => \App\StaticCaching\SiteInvalidator::class,
'rules' => [],
],- Use a queue driver in production. Statamic dispatches invalidation jobs to the queue. Without a queue driver, invalidation runs synchronously inside the CP save request.
- Use Redis (or another fast cache driver). The block index is stored as a single serialised entry. A fast driver reduces index rebuild time.
- The index is built once per cache miss. Subsequent invalidations reuse the cached index with no database queries.
See CHANGELOG.md.
Proprietary — Rox Digital.