drupal-cache-tags
Drupal Cache Tags
Cache tags enable targeted cache invalidation across all cache bins when underlying data changes.
When to Use
- Implementing cache invalidation for custom content
- Adding dependencies to render arrays
- Debugging why content doesn't update after edits
- Creating custom tag schemes for external data
Tag Naming Conventions
| Type | Pattern | Examples |
|---|---|---|
| Entity | entity_type:id |
node:1, user:5, taxonomy_term:42 |
| Entity list | entity_type_list |
node_list, user_list |
| Bundle list | entity_type_list:bundle |
node_list:article, node_list:page |
| Config | config:config_name |
config:system.site, config:views.view.frontpage |
| Custom | module:identifier |
weather:location:milan, stock:AAPL |
Step-by-Step: Adding Cache Tags
- Identify data dependencies in your render array
- Get tags from entities via
$entity->getCacheTags() - Add tags to
#cacheproperty - For custom data, define meaningful tag names
- Invalidate tags when data changes
Examples
Example 1: Basic Entity Tags
Input: "I'm rendering a node title, how do I add cache tags?"
Output:
$build['title'] = [
'#markup' => $node->getTitle(),
'#cache' => [
'tags' => $node->getCacheTags(), // Returns ['node:123']
],
];
Example 2: Multiple Dependencies
Input: "My block shows node info plus site name, what tags do I need?"
Output:
use Drupal\Core\Cache\Cache;
$build = [
'#markup' => $node->getTitle() . ' | ' . $config->get('name'),
'#cache' => [
'tags' => Cache::mergeTags(
$node->getCacheTags(), // ['node:123']
['config:system.site'] // Site name dependency
),
],
];
Example 3: List Tags for Views/Listings
Input: "I have a custom block listing recent articles, what tags should I use?"
Output:
$build = [
'#theme' => 'item_list',
'#items' => $article_titles,
'#cache' => [
'tags' => ['node_list:article'], // Invalidates when ANY article changes
],
];
Example 4: Custom Service with Caching
Input: "I'm caching computed data based on a node, how do I set it up?"
Output:
public function getComputedData(int $nid): array {
$cid = 'my_module:computed:' . $nid;
$cache = \Drupal::cache('data');
if ($cached = $cache->get($cid)) {
return $cached->data;
}
$data = $this->expensiveComputation($nid);
$cache->set($cid, $data, Cache::PERMANENT, [
'node:' . $nid, // Invalidate when node changes
'my_module:computed', // Custom tag for bulk invalidation
]);
return $data;
}
Example 5: Programmatic Invalidation
Input: "External API data changed, how do I invalidate related caches?"
Output:
use Drupal\Core\Cache\Cache;
// Invalidate all caches tagged with these tags (cross-bin)
Cache::invalidateTags(['my_module:external_api', 'my_module:weather']);
// Entity saves automatically invalidate - no manual call needed
$node->save(); // Automatically invalidates node:ID and node_list tags
Example 6: Block with Entity Dependency
Input: "My block depends on the current node, how do I add tags dynamically?"
Output:
class RelatedContentBlock extends BlockBase {
public function build() {
$node = \Drupal::routeMatch()->getParameter('node');
return [
'#markup' => $this->getRelatedContent($node),
];
}
public function getCacheTags() {
$tags = parent::getCacheTags();
$node = \Drupal::routeMatch()->getParameter('node');
if ($node) {
$tags = Cache::mergeTags($tags, $node->getCacheTags());
}
return $tags;
}
}
Common Mistakes
| Mistake | Problem | Fix |
|---|---|---|
my_module:all |
Too broad, invalidates everything | Use specific IDs: my_module:item:123 |
| Missing list tags | New content doesn't appear in listings | Add entity_type_list tag |
| Forgetting config | Theme changes don't reflect | Add config:block.block.X |
| Manual entity invalidation | Redundant, Drupal handles it | Remove manual Cache::invalidateTags() on entity save |
Debugging
# Enable debug headers
$settings['http.response.debug_cacheability_headers'] = TRUE;
# Check X-Drupal-Cache-Tags header in response
curl -sI https://site.com/node/1 | grep X-Drupal-Cache-Tags
# Invalidate specific tag via drush
drush cache-tag-invalidate node:1
More from sparkfabrik/sf-awesome-copilot
drupal-cache-debugging
Drupal cache debugging techniques and troubleshooting workflows. Use when asked about X-Drupal-Cache headers interpretation, finding max-age 0 sources, WebProfiler usage, cache hit/miss analysis, stale content debugging, or performance profiling cache-related issues.
19drupal-cache-contexts
Drupal cache contexts implementation guide. Use when asked about request-based cache variations, user.roles vs user context, URL contexts, language contexts, custom cache contexts, or cache context hierarchy. Helps prevent cache explosion from overly broad contexts.
19drupal-lazy-builders
Drupal lazy builders and placeholder implementation. Use when asked about #lazy_builder render array property, TrustedCallbackInterface, auto-placeholdering, BigPipe integration, personalized content caching, or how to make user-specific content cacheable.
17drupal-cache-maxage
Drupal cache max-age configuration and behavior. Use when asked about time-based cache expiration, Cache::PERMANENT, max-age 0 issues, why Page Cache ignores max-age, or when content appears stale despite time expiration. Critical for understanding caching layer differences.
16drupal-dynamic-cache
Dynamic Page Cache and BigPipe module behavior in Drupal. Use when asked about authenticated user caching, auto-placeholdering, lazy builders, BigPipe streaming, X-Drupal-Dynamic-Cache header, or why content shows UNCACHEABLE status. Covers interaction between caching layers.
15http-cache-tools
HTTP cache debugging tools and techniques. Use when asked to inspect cache headers, debug HTTP responses, use curl for cache analysis, or verify caching behavior. Includes SparkFabrik container context with make drupal-cli and docker compose commands.
11