Cache Contexts
When to Use
When cached content varies based on request context. Cache contexts create separate cache entries for different variations — e.g., one cached version per language, per user role, per URL path.
User Contexts
Context Token: user, user.roles, user.permissions:{permission}
| Context | Varies by... | Use when... |
|---|---|---|
user |
User ID | Content is personalized per-user (profile page, dashboard) |
user.roles |
User's role(s) | Content differs by role (admin tools visible to admins) |
user.permissions:{permission} |
Specific permission | Content gated by permission (edit links for editors) |
// Vary by user ID (separate cache per user)
$build = [
'#cache' => ['contexts' => ['user']],
];
// Vary by user roles (one cache per role combination)
$build = [
'#cache' => ['contexts' => ['user.roles']],
];
Gotchas:
- user context creates cache entry per user — can explode cache size, use sparingly
- user.roles creates entry per unique role combination — user with 5 roles creates different cache key than user with 3 roles
- Anonymous users share user:0 — safe to cache heavily
- Don't use user when user.roles or user.permissions is sufficient
URL Contexts
Context Token: url, url.path, url.query_args, url.site
| Context | Varies by... | Use when... |
|---|---|---|
url |
Full URL including query string | Content changes based on entire URL |
url.path |
Path only (no query string) | Content differs per page but not by query params |
url.query_args |
All query parameters | Content uses query string filtering |
url.query_args:{key} |
Specific query param | Content uses specific param (e.g., ?page=) |
url.site |
Domain/subdomain | Multisite with different content per domain |
// Vary by path only
$build = [
'#cache' => ['contexts' => ['url.path']],
];
// Vary by specific query parameter
$build = [
'#cache' => ['contexts' => ['url.query_args:page']],
];
Gotchas:
- url includes query args — cache miss on every unique URL
- Pagers automatically add url.query_args:page context
- url.path is language-aware when language is in URL
Language Contexts
Context Token: languages:{type}
| Context | Varies by... | Use when... |
|---|---|---|
languages:language_interface |
UI language | Translatable UI strings in cached content |
languages:language_content |
Content language | Cached content includes translated entities |
languages:language_url |
URL language detection | Language determined from URL path prefix |
$build = [
'#cache' => ['contexts' => ['languages:language_interface']],
];
Gotchas: - Multiple language types can be active — use the right one for your content - Translated entities add language context automatically
Route Contexts
Context Token: route, route.name, route.menu_active_trails:{menu}
| Context | Varies by... | Use when... |
|---|---|---|
route |
Route name + parameters | Content tied to current route (breadcrumbs, local tasks) |
route.name |
Route name only | Content varies by route but not route params |
route.menu_active_trails:{menu} |
Active menu trail | Highlighting active menu items |
$build = [
'#cache' => ['contexts' => ['route']],
];
Gotchas:
- route can create many cache variations with dynamic routes
- Menu active trails context needed for "active" CSS classes in menus
Theme Context
Context Token: theme
| Context | Varies by... | Use when... |
|---|---|---|
theme |
Active theme name | Cached content includes theme-specific markup/assets |
$build = [
'#cache' => ['contexts' => ['theme']],
];
Gotchas: - Usually not needed — render arrays are theme-agnostic by default - Only add when caching theme-specific output (e.g., preprocess output)
Session Context
Context Token: session, session.exists
| Context | Varies by... | Use when... |
|---|---|---|
session |
Session ID | Content stored in session (shopping cart) |
session.exists |
Whether session exists | Content differs for users with/without session |
$build = [
'#cache' => ['contexts' => ['session.exists']],
];
Gotchas:
- session context prevents Page Cache (creates session for anonymous users)
- Use session.exists for "logged in" indicator without per-session cache
- Avoid session contexts when possible — they reduce cacheability
Timezone Context
Context Token: timezone
| Context | Varies by... | Use when... |
|---|---|---|
timezone |
User timezone setting | Displaying dates/times in user's timezone |
$build = [
'#cache' => ['contexts' => ['timezone']],
];
Gotchas: - Creates cache entry per unique timezone — can be many variations - Usually handled by date formatters automatically
Request Format Context
Context Token: request_format
| Context | Varies by... | Use when... |
|---|---|---|
request_format |
Request format | Content served in multiple formats |
$build = [
'#cache' => ['contexts' => ['request_format']],
];
Gotchas: - REST module adds this automatically - Rarely needed in custom code
Common Mistakes
- Adding too many contexts — Creates excessive cache variations, cache never hits
- Using
userwhenuser.roleswould work — Creates per-user cache instead of per-role - Forgetting contexts when content varies — Same cached content served to all users incorrectly
- Using
urlinstead ofurl.path— Query strings create infinite cache variations - Not using
Cache::mergeContexts()when combining — Manual array merging doesn't optimize context order
See Also
- Cache Metadata Trinity — How contexts fit into cache metadata
- Best Practices & Patterns — Context selection best practices
- Reference: Cache contexts documentation
- Reference:
/core/lib/Drupal/Core/Cache/Context/directory for all context classes