Cache tags
Cache tags = data dependencies
Cache tags describe dependencies on data managed by Drupal
Why?
Cache tags provide a declarative way to track which cache items depend on specific pieces of data managed by Drupal.
This is important for a Content Management System/Framework like Drupal because the same content can be reused in many different ways. In other words: it’s impossible to know in advance where a particular piece of content will be used. Anywhere content is used, it can be cached. This means the same content can be cached in dozens of places. Which brings us to the famous quote: “There are only two hard things in Computer Science: cache invalidation and naming things.” — that is, how are you going to invalidate all cache items where the content is used?
Note: Drupal 7 offers three ways to invalidate cache items: invalidate a specific CID, invalidate by CID prefix, or invalidate everything in a cache bin. None of these three methods allow invalidating cache items containing a modified entity because it was impossible to know that!
What?
A cache tag is a string.
Cache tags are passed as unordered sets of strings, so they are represented as string[]. These are sets because a single cache item can depend on multiple cache tags.
Syntax
By convention, they take the form thing:identifier — and when there’s no concept of multiple instances of a thing, it takes the form thing. The only rule is that it cannot contain spaces.
There is no strict syntax.
Examples:
- node:5 — cache tag for Node 5 (invalidated whenever it changes)
- user:3 — cache tag for User 3 (invalidated whenever it changes)
- node_list — cache tag for the list of Node entities (invalidated whenever any Node is updated, deleted, or created — i.e., when the node list may need updating). Applicable to any entity type in the format {entity_type}_list.
- config:system.performance — cache tag for the system.performance configuration
- library_info — cache tag for asset libraries
Common cache tags
Data managed by Drupal falls into three categories:
- entities — they have cache tags of the form <entity type ID>:<entity ID>, as well as <entity type ID>_list and <entity type ID>_list:<bundle> for invalidating entity lists. Configuration entity types use the base configuration object’s cache tag.
- configuration — they have cache tags in the form config:<configuration name>
- custom (for example, library_info)
Drupal automatically provides cache tags for entities and configuration — see the Entity base class and ConfigBase class. (All specific entity types and configuration objects inherit from these.)
Although many object types follow the predictable cache tag format <entity type ID>:<entity ID>, third-party code should not rely on this. Instead, it should obtain cache tags for invalidation for a specific object using the ::getCacheTags()
method, for example: $node->getCacheTags()
, $user->getCacheTags()
, $view->getCacheTags()
, and so on.
Additionally, you may need to invalidate caches based on lists that depend on data from the entity in question (for example, refreshing rendered HTML for a listing when a new entity is created for it). This can be done using EntityTypeInterface::getListCacheTags()
, then invalidating all tags returned by that method along with the entity’s own tags. Starting from Drupal 8.9 (change notice), entities with bundles also automatically have a more specific cache tag that includes their bundle to provide more targeted list invalidation.
It’s also possible to define custom, more specific cache tags based on the values entities have — for example, a taxonomy term reference field for lists showing entities tagged with a particular term. Invalidation for such tags can be placed in custom entity presave/delete hooks:
function yourmodule_node_presave(NodeInterface $node) { $tags = []; if ($node->hasField('field_category')) { foreach ($node->get('field_category') as $item) { $tags[] = 'mysite:node:category:' . $item->target_id; } } if ($tags) { Cache::invalidateTags($tags); } }
These tags can be used in code and in Views using the Views Custom Cache Tag module.
Note: Currently there is no API for retrieving individual bundles and more specific cache tags from an entity or another object. This is because it’s not the entity itself that decides which list cache tags are relevant for a specific list/query — that depends on the query. Future versions of Drupal core will likely improve built-in support for bundle-level cache tags and, for example, integrate them into the entity query builder and Views.
How
Setup
Any cache backend must implement CacheBackendInterface. Therefore, when setting a cache item using the ::set()
method, specify the third and fourth arguments, for example:
$cache_backend->set( $cid, $data, Cache::PERMANENT, ['node:5', 'user:7'] );
This stores a cache item with ID $cid
permanently (i.e., indefinitely), but makes it subject to invalidation through the cache tags node:5
or user:7
.
Invalidation
Cache items are invalidated through their tags using cache_tags.invalidator:invalidateTags() (or, if you can’t inject the cache_tags.invalidator
service, Cache::invalidateTags()
), which accepts a set of cache tags (string[]).
Note: This invalidates items tagged with the given tags across all cache bins. It doesn’t make sense to invalidate cache tags per individual bin, since the changed data whose cache tags are invalidated might be depended on by cache items in other bins as well.
Debugging
All of the above is useful when debugging anything that’s cached. But there’s one more thing: suppose something is cached with the cache tags ['foo', 'bar']. Then the corresponding cache item will have a tags column (assuming a database cache) with the following value:
bar foo
In other words:
- cache tags are space-separated
- cache tags are alphabetically sorted
This should make analyzing and debugging caches easier!
Headers (debugging)
Finally: it’s easy to see which cache tags a specific response depends on (and therefore when it will become invalidated): simply look at the X-Drupal-Cache-Tags header!
(This is why spaces are forbidden: because the X-Drupal-Cache-Tags header, like many HTTP headers, uses spaces to separate values.)
Note: If you don’t see these headers, you need to configure your Drupal instance for development.
Integration with Reverse Proxies
Instead of caching responses in Drupal and invalidating them with cache tags, you can also cache responses in reverse proxies (Varnish, CDN, etc.) and then invalidate the responses they cached using cache tags associated with those responses. To let those reverse proxies know which cache tags are associated with each response, you can send the cache tags along with a header.
Just like Drupal 8 can send an X-Drupal-Cache-Tags header for debugging, it can also send a Surrogate-Keys header with space-separated values (as expected by some CDNs), or a Cache-Tag header with comma-separated values (as expected by others). And this can also be a reverse proxy you operate yourself, not necessarily a commercial CDN.
In general, both your web server and your reverse proxy should support response headers up to 16 KB in size.
1. HTTP is text-based, so cache tags are text-based too. Reverse proxies are free to represent cache tags using other data structures. The 16 KB response header limit was chosen for two reasons: A) to ensure it works in 99% of cases, and B) it’s a practical limit. Typical web servers (Apache) and CDNs (like Fastly) support 16 KB response header values. This translates to about 1000 cache tags — sufficient for 99% of cases.
2. The number of cache tags varies greatly depending on the site and specific response. If a response depends on many things, it will have many cache tags. More than 1000 cache tags per response is rare.
3. However, this guideline (~1000 tags/response) may evolve over time as A) more real-world applications use it, and B) systems begin to specifically leverage this capability.
Finally, having more than 1000 cache tags likely indicates a deeper problem: the response is too complex and should be split. Nothing prevents you from exceeding that number in Drupal, but it may require manual tuning — which is acceptable for very complex use cases. This can even apply to far fewer than 1000 cache tags.
Read the documentation on using Varnish with cache tags.
Known CDNs that support tag-based invalidation/purging:
CloudFlare
Fastly
KeyCDN
Akamai
Internal Page Cache
The comprehensive use of cache tags in Drupal 8 makes it possible to ship Drupal 8 with the Internal Page Cache module enabled by default. This is essentially a built-in reverse proxy.