# Drupal Cache Tags

> Drupal cache tags implementation guide. Use when asked about cache tag naming conventions, entity tags, list tags, custom tags, tag invalidation strategies, or debugging tag-based cache invalidation issues. Covers node:ID, config:name, entity_list patterns. Use when this capability is needed.

- Skill: `tomevault-io/drupal-cache-tags` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/drupal-cache-tags`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/drupal-cache-tags/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/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

1. Identify data dependencies in your render array
2. Get tags from entities via `$entity->getCacheTags()`
3. Add tags to `#cache` property
4. For custom data, define meaningful tag names
5. 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:**
```php
$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:**
```php
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:**
```php
$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:**
```php
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:**
```php
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:**
```php
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

```bash
# 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
```

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/sparkfabrik) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-13 -->

