Editorial illustration; the request, context and cache labels are representative examples, not a live Drupal installation.
A Drupal page can be fast and still be wrong. A menu intended for an editor appears to an anonymous visitor, one language is shown to every reader, or a block personalised for one user is reused for another. These failures happen when cached output does not describe the request context that changes it.
Drupal’s Cache API separates three ideas: cache tags describe what invalidates a result, max-age describes how long it can live, and cache contexts describe which request conditions create different variants. This article focuses on the third idea and a workflow for finding the smallest correct variation.
Summary
- Add a cache context when the rendered output changes according to a request condition such as user role or language.
- Do not use a broad context as a guess; high-cardinality variation can reduce cache usefulness.
- Inspect cacheability metadata and test anonymous, authenticated, language, route and permission cases before shipping.
What is a cache context?
A cache context is a named way for Drupal to vary cached output. If a render array depends on the current user’s permissions, the output cannot be safely shared across all users. If it depends on interface language, the language context matters. The context is metadata attached to the render result so Drupal can keep variants separate.
This is different from a cache tag. A tag answers “which content or configuration change should invalidate this?” A context answers “which request variation should receive a different result?” Max-age answers “when should this expire?” Confusing these questions creates bugs that clearing caches can temporarily hide.
A decision workflow
1. Identify the changing output
Look at the smallest component that differs. Is it a block, a link, a label, a field formatter or the whole page? Avoid varying the whole response if only one small fragment is personalised. Drupal’s render system can use placeholders for highly dynamic parts when the render structure supports it.
2. Name the actual input
Write a sentence such as “This label changes by interface language” or “This link appears only when the current user has permission X.” That sentence suggests the context to investigate. Do not add user merely because the output feels dynamic; if the decision is based on permissions or role, use the narrower supported context when appropriate.
3. Attach metadata at the render boundary
Custom code should add cacheability metadata where the output is created, not as a late global patch. The exact API depends on whether you are working with a render array, a response or a cacheable dependency. Use Drupal’s API documentation for the version you run, then keep the dependency close to the component that needs it.
The principle is simple: if a component reads a value that varies by context, its cacheability metadata must describe that dependency before it bubbles into the parent render result.
4. Inspect the result in development
Drupal documentation describes headers such as X-Drupal-Cache-Contexts, X-Drupal-Cache-Tags and X-Drupal-Cache-Max-Age as useful when cacheability data is enabled for development. Inspect them while changing one condition at a time. If you expected language variation but the header does not reflect it, the metadata is incomplete. If you see a broad or high-cardinality context, ask whether the component truly needs it.
5. Test the boundaries
Use at least two users with different permissions, two languages where enabled, anonymous and authenticated sessions, and a route that should not match. Clear caches before the first controlled test, but do not treat repeated clearing as proof. A correct result should remain correct after the cache warms again.
Example: a role-sensitive link
Imagine a dashboard block that shows “Review submissions” only to users with a moderation permission. The output is not the same for everyone. A page-level cache that ignores the relevant access context could reuse the link for an anonymous visitor or hide it from a permitted reviewer.
Start by confirming the access check itself. Then identify the cacheability metadata expected by the access result and the block’s render output. Test an anonymous browser, a normal authenticated user and a reviewer account. Inspect both the visible result and the cacheability headers in development.
Do not solve the problem by disabling caching for the whole page unless the page is genuinely uncacheable. A small dynamic fragment may be better represented as a placeholder or a separately varied component.
Why broad variation hurts
The user context can create a large number of variants. If a block only differs by interface language, varying by user throws away sharing without improving correctness. If a component truly differs by individual user, that cost may be necessary, but it should be deliberate and measured.
A common debugging mistake is to add more contexts until the wrong output disappears. That may make the symptom less visible while making the cache less effective. First identify the input that changes the output; then choose the narrowest correct context supported by the Drupal version and component.
Cache contexts and reverse proxies
Drupal’s internal cache metadata is not a substitute for understanding the caches in front of it. A reverse proxy or CDN may have its own cache key and header rules. If a proxy ignores the variation signals that Drupal emits, it can serve the wrong representation before Drupal gets a chance to render it.
Test through the same path real visitors use. A local authenticated test may be correct while the public edge cache still serves an old or shared response. Coordinate cache-control and variation behaviour across the application and edge layers, and document any intentional bypasses.
For adjacent workflows, see how to troubleshoot Drupal cache without clearing everything and how Drupal Search API design begins with an index.
Mistakes to avoid
- Treating cache tags as if they describe user or language variation.
- Clearing every cache instead of inspecting the missing metadata.
- Varying by the entire user object when a narrower permission or role decision is enough.
- Testing only as an administrator and assuming anonymous output is correct.
- Checking Drupal’s internal response while ignoring a CDN or reverse proxy cache key.
My Take
Cache correctness is a data-dependency problem. Once you state exactly which input changes a component, the choice of context becomes much easier to review. The fastest long-term fix is usually better metadata and a small test matrix, not a permanent cache bypass.
Conclusion
Use cache contexts to describe request conditions that change rendered output, keep the variation as narrow as correctness allows, and verify the result after the cache warms. Separate contexts from tags and max-age, inspect development metadata, and test through every cache layer that serves the site.