Drupalwoo ☉

How to use Drupal’s cache tags for granular invalidation

Drupal’s cache tags provide a precise way to remove cached data when its underlying content changes. Instead of clearing every page after editing one node, you can associate a rendered fragment, response, or custom cache item with tags such as node:42 or taxonomy_term:8. Drupal then invalidates items carrying the affected tag.

This approach is valuable for content-heavy websites, commerce platforms, intranets, and publishing workflows. A news site in Sydney, a council website in Melbourne, or an Australian retailer with frequently changing stock can keep pages fast while ensuring that related content is refreshed promptly.

Cache mechanism Main purpose Typical invalidation behaviour Suitable use
Cache tags Identify related cached items Remove items sharing a changed tag Content and entity dependencies
Cache contexts Vary output by request conditions Keep separate variants User, language, device, or URL differences
Cache max-age Set a time limit Expire after a duration Data that becomes stale predictably
Cache bins Organise stored cache items Clear a complete category Render cache, dynamic page cache, custom storage
Manual cache clear Remove broad cached data Flushes selected or all caches Maintenance and troubleshooting

What cache tags represent

A cache tag is a string attached to a cacheable item. The string identifies the data that item depends on. Drupal core commonly generates tags for content entities, including nodes, taxonomy terms, users, files, and media entities. A node with ID 42 normally has the tag node:42.

When Drupal renders a node title, a View result, or a block containing entity data, cacheability metadata can add the relevant tags automatically. If an editor updates node 42, Drupal invalidates node:42. Any cached item carrying that tag becomes unusable and is rebuilt when requested.

Tags do not store the cached content themselves, and they do not control how long an item remains valid. They describe relationships between cached output and source data. This distinction is important because tags work alongside cache contexts and maximum age settings rather than replacing them.

How invalidation works in Drupal

Drupal’s cache system usually invalidates items lazily. When a tag is invalidated, Drupal records that the tag has changed. Existing entries may remain in the backend temporarily, but Drupal will treat entries with that tag as expired. The next request rebuilds the output and stores fresh metadata.

Entity storage automatically invalidates common entity tags when entities are created, updated, or deleted. For example, saving a product node can invalidate the product’s entity tag, its list tag, and related tags generated by the entity type. A page displaying that product will then be rebuilt when needed.

This model avoids the cost of deleting every item from Redis, the database, or another cache backend. It is especially useful for busy Australian sites where traffic peaks around local events, lunch breaks, or evening shopping periods in markets such as Brisbane and Perth. Unrelated pages can continue serving cached responses while affected content is refreshed.

Reading cacheability metadata

Rendered output carries cacheability metadata through Drupal’s render API. The metadata includes cache tags, cache contexts, and max-age. You can inspect it with the #cache property on a render array.

$build['notice'] = [
  '#markup' => $this->t('Store information'),
  '#cache' => [
    'tags' => ['store:brisbane'],
    'contexts' => ['url.path'],
    'max-age' => 3600,
  ],
];

This example says that the markup depends on a custom Brisbane store record, varies by the current path, and can remain valid for one hour. If the store record changes, invalidating store:brisbane removes the relevant cached version before the hour has elapsed.

For entity-based render arrays, prefer using the entity’s cache metadata instead of manually guessing tags. Drupal provides cacheable dependencies that allow an entity and its metadata to be attached safely.

$build['product'] = $this->entityTypeManager
  ->getViewBuilder('node')
  ->view($node, 'teaser');

$build['product']['#cache']['tags'][] = 'stock:item:482';

The view builder contributes the node’s standard tags. The additional stock tag links the output to an external inventory record.

Adding tags to custom data

Custom services and controllers need explicit cache metadata when they return data that Drupal cannot identify automatically. A controller returning a render array can add tags directly.

public function dashboard(): array {
  $build = [
    '#theme' => 'customer_dashboard',
    '#items' => $this->dashboardManager->getItems(),
  ];

  $build['#cache']['tags'] = ['customer_dashboard'];
  $build['#cache']['contexts'] = ['user'];
  $build['#cache']['max-age'] = 900;

  return $build;
}

The user cache context is essential if the dashboard differs for each logged-in account. Without it, one user’s output could be reused for another user. The customer_dashboard tag gives the application a stable identifier to invalidate later.

For a cache item stored directly in a cache bin, add tags to the item’s metadata:

$cache_data = [
  'value' => $records,
  'created' => \Drupal::time()->getRequestTime(),
];

$this->cacheBackend->set(
  'regional_events',
  $cache_data,
  CacheBackendInterface::CACHE_PERMANENT,
  ['event:regional', 'event:482']
);

A service can invalidate the related records when an import finishes:

$this->cacheTagsInvalidator->invalidateTags([
  'event:regional',
  'event:482',
]);

Inject the cache backend and tag invalidator through the service container in production code. Static service calls are convenient for small demonstrations, but dependency injection makes custom code easier to test and maintain.

Choosing tag names for external systems

A useful custom tag should identify a stable dependency rather than a temporary request. Namespaces help prevent collisions. Examples include weather:brisbane, stock:item:482, campaign:summer-sale, and crm:contact:901.

If an external API supplies a list of events for every Australian state, use both collection and item tags when appropriate. A page showing the complete list might carry events:all, while a page showing one event carries event:482. An update to one event can invalidate the item-specific output, while a complete feed refresh can invalidate the collection tag.

$this->cacheTagsInvalidator->invalidateTags([
  'event:482',
  'events:all',
]);

Avoid placing user-specific values in tags when the content should vary by user. A tag such as account:123 can be appropriate for account-owned data, but it does not replace the user cache context. Tags answer “what data changed?” Contexts answer “for which request variations should Drupal store separate output?”

Working with Views, blocks, and page caches

Views often receive entity cache tags through their query and rendered results, although the exact metadata depends on the display and row style. A View listing nodes should generally be invalidated when a listed node changes. If a custom query bypasses Drupal’s entity and render systems, inspect its cacheability and attach tags manually.

Custom blocks follow the same principle. A block showing the latest grant announcements might depend on a View, taxonomy term, or external feed. Attach the source entity tags or a custom feed tag to the block’s render array. A page using Dynamic Page Cache can then reuse the block until its dependency changes.

The full page may have multiple cache layers. Internal Page Cache is intended primarily for anonymous responses, while Dynamic Page Cache can cache pages with broader variation rules. Reverse proxies such as Varnish or a managed CDN may sit in front of Drupal. Drupal cache tags can invalidate Drupal’s own caches, but the proxy must be configured to understand purge requests if stale HTML is stored there.

For an organisation serving visitors in Adelaide and Canberra, this distinction matters during urgent content updates. Publishing an emergency notice can correctly invalidate Drupal’s tagged response while an external CDN continues serving an older copy unless its purge integration is working.

Testing and troubleshooting invalidation

Enable cacheability headers in a development environment to inspect metadata. Drupal can expose headers such as X-Drupal-Cache-Tags, X-Drupal-Cache-Contexts, and X-Drupal-Cache-Max-Age, depending on configuration and version. Browser developer tools or command-line requests can reveal whether a response carries the expected dependency.

curl -I https://example.com/news

When a page does not refresh after an entity update, check whether the affected entity tag appears in the response or render metadata. Then verify that the update operation actually triggers the expected invalidation. A custom database update, direct SQL statement, or external import may bypass entity storage hooks and require explicit invalidation.

A missing cache context can look like a tag problem. If output changes by language, URL, permissions, or user but the response is identical for everyone, add the appropriate context. If output is identical until a fixed time expires, add a tag or reduce max-age according to the data source.

Development cache clearing can hide mistakes because drush cr removes broad categories of cached data. Test the real update path separately: create or edit the dependency, request the affected page, and confirm that unrelated pages retain their cached state. Logging tag invalidation during an import can also expose naming mismatches such as invalidating stock:482 while the cached item uses stock:item:482.

Building a reliable invalidation workflow

Start by listing the data sources used by a renderable item. For each source, decide whether Drupal already supplies entity metadata or whether the application needs a custom tag. Then identify request differences and add cache contexts for values such as language, URL, roles, or user identity.

Keep invalidation close to the operation that changes the data. A product synchronisation service should invalidate product and stock tags after a successful update, rather than relying on a scheduled full cache clear. A content migration should invalidate affected entity tags once records are saved, with collection tags added when list pages depend on the changed dataset.

Use broad tags sparingly. A tag such as catalogue:all is simple, but invalidating it for every product update can cause unnecessary rebuilds across a large store. Combining collection pages with item-specific tags gives better control and reduces cache churn.

Finally, document custom tag conventions for the development team. Consistent names make imports, queue workers, moderation workflows, and scheduled jobs easier to reason about. Granular invalidation then becomes part of the site’s content architecture: editors can update a page in Sydney, a store listing in Hobart, or a campaign for the Australian market without forcing a complete cache flush.