drupal-cache-contexts
Drupal Cache Contexts
Cache contexts define request-dependent cache variations. Analogous to HTTP Vary header.
When to Use
- Content varies by user role or permissions
- Content changes based on URL parameters
- Multi-language sites with localized content
- Theme-dependent rendering
- Preventing cache explosion from overly broad contexts
Available Contexts
| Context | Variations | Use Case |
|---|---|---|
user |
Per-user (AVOID) | Truly personalized content only |
user.roles |
Per-role combination | Role-based visibility |
user.permissions |
Per-permission set | Permission-based content |
url.path |
Per-path | Path-dependent content |
url.query_args:key |
Per-parameter | Sort, filter, pagination |
languages:language_interface |
Per-language | Translated content |
theme |
Per-theme | Theme-specific rendering |
session |
Per-session | Session data (triggers placeholder) |
Context Hierarchy
user (AVOID - per-user cache explosion)
├── user.is_super_user (2 variations only)
├── user.roles (per role combination)
│ └── user.roles:editor (has specific role?)
└── user.permissions (per permission set)
Rule: Always use the most specific context possible.
Examples
Example 1: Role-Based Content
Input: "I show different content to editors vs anonymous users"
Output:
$build = [
'#markup' => $this->getRoleBasedContent(),
'#cache' => [
'contexts' => ['user.roles'],
],
];
Example 2: Per-User Content (Use Sparingly)
Input: "I need to show the user's own profile info"
Output:
// WARNING: Creates cache entry per user - use lazy_builder instead
$build = [
'#markup' => $user->getDisplayName(),
'#cache' => [
'contexts' => ['user'], // High cardinality!
],
];
// BETTER: Use lazy builder for per-user content
$build = [
'#lazy_builder' => ['my_module.lazy:userName', []],
'#create_placeholder' => TRUE,
];
Example 3: URL Query Parameters
Input: "My listing supports ?sort=date and ?sort=title parameters"
Output:
$sort = \Drupal::request()->query->get('sort', 'date');
$build = [
'#theme' => 'item_list',
'#items' => $this->getSortedItems($sort),
'#cache' => [
'contexts' => ['url.query_args:sort'],
],
];
Example 4: Language-Dependent Content
Input: "I render translated labels in my block"
Output:
$build = [
'#markup' => $this->t('Welcome'),
'#cache' => [
'contexts' => ['languages:language_interface'],
],
];
Example 5: Combined Contexts
Input: "Content varies by role AND language"
Output:
$build = [
'#markup' => $this->getLocalizedRoleContent(),
'#cache' => [
'contexts' => [
'user.roles',
'languages:language_interface',
],
],
];
Example 6: Custom Cache Context
Input: "I need to vary cache by a custom HTTP header"
Output:
# my_module.services.yml
services:
cache_context.custom_header:
class: Drupal\my_module\Cache\CustomHeaderContext
arguments: ['@request_stack']
tags:
- { name: cache.context }
namespace Drupal\my_module\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
class CustomHeaderContext implements CacheContextInterface {
public static function getLabel() {
return t('Custom header');
}
public function getContext() {
$request = $this->requestStack->getCurrentRequest();
return $request->headers->get('X-Custom-Header', 'default');
}
public function getCacheableMetadata() {
return new CacheableMetadata();
}
}
// Usage
$build['#cache']['contexts'][] = 'custom_header';
Example 7: Block with Cache Contexts
Input: "My block shows different actions based on permissions"
Output:
class ActionBlock extends BlockBase {
public function build() {
$actions = [];
if (\Drupal::currentUser()->hasPermission('edit content')) {
$actions[] = 'Edit';
}
return ['#markup' => implode(', ', $actions)];
}
public function getCacheContexts() {
return Cache::mergeContexts(
parent::getCacheContexts(),
['user.permissions']
);
}
}
Common Mistakes
| Mistake | Impact | Solution |
|---|---|---|
Using user for role checks |
Cache explosion (1 entry per user) | Use user.roles |
Using session directly |
Triggers auto-placeholder | Use lazy builder |
| Missing context | Same cached content for all variations | Add appropriate context |
| Too broad context | Unnecessary cache variations | Use most specific context |
Auto-Placeholdering
These contexts trigger automatic placeholdering in Dynamic Page Cache:
# services.yml - default conditions
renderer.config:
auto_placeholder_conditions:
contexts:
- 'session'
- 'user'
Content with these contexts is replaced with a placeholder and rendered separately.
Debugging
# Enable debug headers
$settings['http.response.debug_cacheability_headers'] = TRUE;
# Check applied contexts
curl -sI https://site.com/ | grep X-Drupal-Cache-Contexts
# Output: X-Drupal-Cache-Contexts: languages:language_interface theme url.path user.permissions
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.
20drupal-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.
18drupal-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.
18drupal-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.
17drupal-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.
16http-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