Picture fifty cached "Recent articles by this author" blocks, one per author on a busy site. Author #23 just edited their bio. Which of those fifty cached blocks needs to be thrown away? Just one — but how would Drupal know which one, out of fifty stored copies, without keeping a manual list somewhere? Cache tags answer exactly this question: label a cached item with what it depends on, and later invalidate everything carrying that label in one call.
What you'll learn in this lesson
- What a cache tag actually is, and Drupal core's naming convention
- How to attach tags to a render array so the render cache invalidates correctly
Cache::invalidateTags()— the call that clears every tagged item, across every cache bin, in one shot
A cache tag is just a string
There's no special "tag" data type in Drupal — a cache tag is a plain string, conventionally shaped entity_type:id, like node:42 or user:7. Drupal core tags things this way automatically: load a node's render array and it already carries node:42 as a cache tag, with zero code from you. Saving that node later triggers Drupal to call Cache::invalidateTags(['node:42']) internally, which busts every page, block, and view anywhere on the site that was tagged as depending on it — without any of that unrelated code needing to know about each other.
Attaching a custom tag to a render array
$build = [
'#markup' => $this->t('Recent articles by @name', ['@name' => $author->getDisplayName()]),
'#cache' => [
'tags' => ['user:' . $author->id(), 'node_list:article'],
],
];
This block is now tagged with both the specific author and the general "any article changed" list tag. Either one being invalidated will correctly expire this cached copy.
Invalidating tags programmatically
use Drupal\Core\Cache\Cache;
Cache::invalidateTags(['user:23']);
Cache::invalidateTags() is a static method that reaches into every registered cache bin at once and marks every entry carrying any of the given tags as invalid — this is the exact mechanism you'll build custom invalidation logic on top of in your own modules, and it's covered in real running code in the Examples module's cache_example, which demonstrates the underlying get()/set() calls this all sits on top of.
invalidateTags() call does the same job with far less collateral damage.Quick check: your custom block shows the count of unread notifications for the current user. What tag would correctly and efficiently invalidate it the moment a notification is marked read? (Something scoped to that specific user, like
user:{$uid}or a customnotifications:{$uid}tag — not a site-wide tag, since that would invalidate the same block for every other user unnecessarily.)
Key takeaways
- A cache tag is a plain string, conventionally
entity_type:id, attached via a render array's#cache['tags']key. Cache::invalidateTags()is a static, cross-bin operation — the right tool for "invalidate everything depending on X," rather than tracking cache IDs by hand.- Reach for the narrowest tag that solves your problem; a full cache flush is a blunt instrument that punishes every visitor, not just the ones affected by the change.
Coming up next
Tags answer "what changed." Next we cover the other half of the puzzle — cache contexts, which answer "does this vary by visitor," so two different users never accidentally see each other's cached content.