Cache tags for Extbase plugins and repository auto-tagging
A cached plugin is fast, but its output must be refreshed when the records it shows change — otherwise visitors keep seeing stale data. The wrong fix is to disable the cache (see Non-cacheable Extbase plugin actions and developer responsibility); the right one is to let the cache stay, and invalidate the caches of affected pages when a record changes.
Cache tags are how TYPO3 does that. Each page-cache entry can be tagged with the records it depends on; clearing a tag flushes every page tagged with it. Extbase can attach these tags for you automatically, so that saving a record through a repository refreshes exactly the pages that display it.
On this page
See also
This page covers only how Extbase produces and uses cache tags. For the tagging mechanism itself — how the page cache stores tags and how flushing by tag works — see Working with cache tags.
Automatic cache tagging of Extbase repository results
When a repository query reads records, Extbase can tag the current page cache entry with each record it returned. The tag identifies the table and the record by UID, in the form:
<tablename>_<uid>
For a conference with UID 42 in the table
tx_,
the tag is
tx_. The page that
rendered the conference list is tagged with one such tag per conference it
displayed. Each tag also carries a lifetime: a default lifetime is attached, and
it is shortened when the record has a
starttime
or
endtime
, so the
page cache expires no later than the record's own visibility window.
Because the tagging is driven by what the query actually returned, you do not maintain any tag list yourself: add a record to the list, and the page that shows the list is tagged with it; change that record later, and clearing its tag flushes the list page automatically.
Enabling automatic cache tagging in Extbase
Automatic tagging is controlled by the
frontend.
feature toggle. When it is enabled, repository reads tag the page cache as
described above; when it is disabled, no tags are added and you must invalidate
caches another way — for example with automatic cache clearing or by tagging manually.
Warning
The default of
frontend. depends on how the instance
was created. A fresh TYPO3 v14 installation has it enabled; an instance
upgraded from an earlier version keeps it disabled, because feature
toggles retain their existing value across an upgrade. An upgraded site
therefore does not get automatic cache tagging until you switch the toggle
on explicitly.
Changed in version 14.0
frontend. defaults to enabled on fresh
installations. Upgraded instances keep their previous value — verify the
toggle after upgrading. See
Check relevant feature toggle defaults after upgrading (TYPO3 v14).
You set the toggle in settings.php under
SYS/, or through Admin Tools > Settings > Feature
Toggles in the backend.
Adding cache tags manually in an Extbase controller
Automatic tagging covers the records a repository query returns. When a plugin's output depends on records that were not the query result — related records reached through a relation, an aggregate, or data fetched outside Extbase — those records produce no tag of their own. Add the tags yourself through the frontend cache collector request attribute:
<?php
namespace MyVendor\MyExtension\Controller;
use MyVendor\MyExtension\Domain\Repository\ConferenceRepository;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Cache\CacheTag;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class ConferenceController extends ActionController
{
public function __construct(
protected readonly ConferenceRepository $conferenceRepository,
) {}
public function listAction(): ResponseInterface
{
$conferences = $this->conferenceRepository->findAll();
// Auto-tagging tags the conferences the query returned, but not the
// related location records. Follow the relation and tag each location
// so the page is flushed when a location changes too:
$cacheCollector = $this->request->getAttribute('frontend.cache.collector');
foreach ($conferences as $conference) {
$location = $conference->getLocation();
if ($location !== null) {
$cacheCollector->addCacheTags(
new CacheTag('tx_myextension_domain_model_location_' . $location->getUid()),
);
}
}
$this->view->assign('conferences', $conferences);
return $this->htmlResponse();
}
}
The example above tags each location by its own UID. Tagging with a table name
alone (
tx_, without a UID) instead tags
the page against every record of that table, so any change in the table flushes
the page. Use a record-specific tag where you can; use the table-wide tag only
when the output genuinely depends on the whole table.
Adjusting an Extbase plugin's page cache tags while it renders
Inside a plugin action you are building content for one page, and the frontend cache collector request attribute controls the cache tags of that page — the one currently being rendered. Adding or removing a tag changes what the page's cache entry depends on and its lifetime; it does not touch any other page.
| Operation | Effect |
|---|---|
| Add tags | Tag the page being rendered with a record it depends on, so a change to that record flushes the page. |
| Remove tags | Drop a tag from the page being rendered that it should not carry. |
Use it to tag the page being built — as in Adding cache tags manually in an Extbase controller above — or to remove a tag it should not carry:
<?php
namespace MyVendor\MyExtension\Controller;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Cache\CacheTag;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class ConferenceController extends ActionController
{
public function listAction(): ResponseInterface
{
$cacheCollector = $this->request->getAttribute('frontend.cache.collector');
$cacheCollector->removeCacheTags(
new CacheTag('tx_myextension_domain_model_location_5'),
);
return $this->htmlResponse();
}
}
Flushing cached pages from Extbase code after data changes
Most of the time you do not flush caches by hand — automatic cache clearing resolves the affected pages from the records you changed and flushes them at the end of the request. You need to act directly only when records change outside a rendering request — for example through an import, a command-line task, an event listener. The pages that display those records were cached earlier and must now be flushed. Inject Extbase's cache-clearing helper, which offers these operations:
| Operation | Effect |
|---|---|
| Clear specific pages | Flush the page cache for one or more page UIDs, by their
page tag. |
| Clear all pages | Flush the entire page cache group. Use sparingly — it discards the cache for the whole site. |
| Register a record for clearing | Record a table and UID whose pages should be flushed at the end of the request. This is what automatic cache clearing uses internally. |
<?php
namespace MyVendor\MyExtension\Service;
use TYPO3\CMS\Extbase\Service\CacheService;
class ConferenceImportService
{
public function __construct(
protected readonly CacheService $cacheService,
) {}
public function flushAfterImport(int $pageId): void
{
// Flush the page cache of specific pages right away:
$this->cacheService->clearPageCache([$pageId]);
// Or register a record so the pages showing it are flushed at the end of
// the request — this is what automatic cache clearing uses internally:
$this->cacheService->clearCacheForRecord('tx_myextension_domain_model_conference', 42);
}
}
Note
This helper is marked
@internal
. Prefer
automatic cache clearing or the frontend
cache collector where they fit; reach for the helper only when you genuinely
need to flush already-cached pages from your own code.
How records trigger cache clearing when they change is the subject of the next page.
See also
- Automatic cache clearing on record changes — how repository writes flush the tagged pages.
- Non-cacheable actions — the alternative you usually do not want; cache tags let you keep the cache instead.