- Cache contexts are used by Magento to deliver different content to different users.
- Using custom flags ensures that you can create separate cache entries without harming site speed.
- Extending Http\Context helps with adding your own dynamic caching conditions.
- You can control content variations by modifying block cache keys.
- For highly personalized data like welcome messages or personal dashboards, use private content.
- To ensure optimal performance after making changes, track the cache hit/miss rates.
Magento’s caching system comes with a trade-off. While it ensures unmatched store performance by serving pre-rendered blocks or pages, merchants have to face complexities when it comes to displaying different content to different users based on dynamic conditions. For example, displaying special offers only to affiliates or modifying the homepage banner based on the user’s location or device becomes a tad complex!
This is where Magento’s cache contexts and custom HTTP flags shine. In this blog, we’ll go through the step-by-step process of creating separate cacheable blocks or pages for any flag data. By the end of this blog, you’ll be all ready to deliver personalized shopping experiences with optimal store performance.
Understanding Magento Cache Context
First, let’s explore how Magento’s caching system uses Varnish tags, keys, and contexts to effectively serve and nullify cached content.
How Magento Caches Blocks/Pages Using cache_contexts, cache_keys, and Varnish Tags
The block and page caches of Magento revolve around these three concepts:
- cache_contexts
- Cache_contexts are called “dimensions” (currency, store view, customer group, etc.), tracked by Magento to determine cache variations. When you define a context, you instruct Magento to treat the cache as different when there’s a change in context value.
- cache_keys
- Magento combines different context values with block-specific identifiers to generate a composite key internally. Then it uses MD5 to hash this array, which helps create a unique string. This key identifies the unique version of the page or block, leading to precise cache retrieval.
- Varnish tags
- Whether served through Varnish or stored in Redis, each cached blob gets tagged (for example, catalog_product_42). When a user makes an update to related content (like saving a product), Magento triggers a purge on that corresponding tag to invalidate the outdated cache entries and serve the fresh content.
Every page or block is able to define the contexts it depends on by implementing getIdentities() and getCacheKeyInfo() within the block class.
Role of layout.xml, Block Classes, and Plugins
- layout.xml
- The <block> node’s cacheable=”true” attribute (which is the default for the majority of structural blocks) signals Magento that the block is eligible for caching. You can also specify a cache lifetime by using the following to control its behavior further:
- <arguments><argument name=”cache_lifetime” xsi:type=”number”>3600</argument></arguments>.
- Block classes
- Cache behavior is driven by two methods:
- getCacheKeyInfo() returns an array of values (contexts + extra bits) to create cache_key.
- getIdentities() returns tags for invalidation.
- Plugins and Observers
- If you want to add or override the context values at runtime, a plugin needs to be registered on
\Magento\Framework\App\Http\Context::getValue()
. You can also listen to an event to tweak the context bag prior to key generation.
For a deeper dive into Magento’s layout system, check out our detailed Magento 2 Layout Overview.
Use Cases
There are various scenarios where you must vary the cache depending on dynamic flags.
Showing Different Content for Logged-in vs Guest
For this, you can use Magento’s built-in customer_logged_in context.
- In your block’s getCacheKeyInfo(), add the following:
$this->httpContext->getValue(\Magento\Customer\Model\Context::CONTEXT_AUTH);
- This will ensure that the guest shoppers see a general view while the logged-in users get a personalized feed, with both of them cached separately.
Switching Banners or Blocks According to User Location, Device, or Custom Flags
- Location (GeoIP)
- Integrate a GeoIP lookup service like MaxMind or other third-party modules to identify user location. Then you can expose a custom context key like user_country. Now, you can put this in your block:
$this->httpContext->getValue(‘user_country’);
This will make sure that customized promos are displayed without hitting the database on each page load.
- Device (Mobile vs Desktop)
- Inspect the User-Agent via a plugin on Http\Context or leverage Magento’s built-in design/theme. Expose a custom is_mobile boolean context, which can replace heavy desktop carousels with lightweight mobile sliders.
- Custom Flags (e.g., is_affiliate_user)
- Let’s say you want to display a “Welcome, Partner!” widget to affiliates. For that, you need to define and populate an is_affiliate_user flag in customer metadata or session, and add it to your cache keys in order to separately cache your affiliate pages from non-affiliate users.
Implementing Flag-Based Cache Variants
Here’s how you can implement the flag-based cache variants.
Step 1: Create Custom Context Class
To inform Magento about your new flag, extend the HTTP context:
<?php
namespace Vendor\Module\Plugin;
use Magento\Framework\App\Http\Context;
use Magento\Customer\Model\Session;
class AffiliateHttpContext
{
const CONTEXT_AFFILIATE = 'is_affiliate_user';
/**
* @var Session
*/
private $customerSession;
/**
* Constructor to inject Magento's Customer Session
*
* @param Session $customerSession
*/
public function __construct(Session $customerSession)
{
$this->customerSession = $customerSession;
}
/**
* After plugin for getValue: injects our affiliate flag.
*
* @param Context $subject
* @param mixed $result
* @param string $key
* @param mixed $default
* @return mixed
*/
public function afterGetValue(Context $subject, $result, $key, $default = null)
{
if ($key === self::CONTEXT_AFFILIATE) {
return $this->detectAffiliate();
}
return $result;
}
/**
* Determines affiliate status from Magento session.
*
* @return bool
*/
private function detectAffiliate(): bool
{
return (bool) $this->customerSession->getIsAffiliateUser();
}
}
Step 2: Register Your Plugin in di.xml
<type name="Magento\Framework\App\Http\Context">
<plugin
name="affiliate_http_context"
type="Vendor\Module\Plugin\AffiliateHttpContext"
sortOrder="10"
disabled="false" />
</type>
After this, any call to $httpContext->getValue(‘is_affiliate_user’) will return a reliable boolean. Also, it will become part of all cache keys wherever you reference it.
Modify Cache Keys in Blocks
Now, it’s time to wire the new context value into a block’s cache key. It makes sure that there are distinct cache buckets for every flag.
<?php
namespace Vendor\Module\Block;
use Magento\Framework\View\Element\Template;
use Magento\Framework\App\Http\Context as HttpContext;
class AffiliateBanner extends Template
{
private HttpContext $httpContext;
public function __construct(
Template\Context $context,
HttpContext $httpContext,
array $data = []
) {
parent::__construct($context, $data);
$this->httpContext = $httpContext;
}
/**
* Add affiliate flag to cache key info.
*
* @return array
*/
public function getCacheKeyInfo(): array
{
return array_merge(
parent::getCacheKeyInfo(),
[
'AFFILIATE_BANNER',
(int) $this->httpContext->getValue('is_affiliate_user')
]
);
}
/**
* Define cache lifetime (optional, defaults to config).
*
* @return int|bool
*/
public function getCacheLifetime()
{
return 3600; // one hour
}
}
Why it works:
- parent::getCacheKeyInfo() pulls in details like customer group, theme, store, etc.
- We track on our custom AFFILIATE_BANNER tag and a 0/1 value.
- Magento generates an MD5 hash, which creates two separate cache entries: one for affiliates and another for other users.
Using Private Content Where Needed
Not all dynamic content works well with full-page caching. Sometimes, you need to use JavaScript.
When the Block Must Be Private
There are some blocks that should never be cached and shared across users:
- Mini-cart totals (vary per session)
- Personalized greetings (“Hi, Davis!”)
- Real-time stock status
Instead of using PHP, load these using the customer-data JavaScript module.
Fallback to JS-Based Rendering
- Define a RequireJS component in requirejs-config.js:
var config = {
map: {
'*': {
'AffiliateWidget': 'Vendor_Module/js/affiliate-widget'
}
}
};
- JS module (affiliate-widget.js):
define(['jquery', 'Magento_Customer/js/customer-data'], function ($, customerData) {
'use strict';
return function(config) {
var section = customerData.get('affiliate-section');
section.subscribe(function(data) {
if (data.isAffiliate) {
$('#affiliate-banner').show();
}
});
};
});
- Server-side: Add affiliate-section data in a block that is already labeled as private.
$customerData->setSectionData(‘affiliate-section’, [‘isAffiliate’ => true]);
It ensures that the widget is only displayed to the logged-in affiliates.
For further insights on optimizing Magento performance, you might find our Webinar Recap on Tuning Magento to Unbelievable Speeds beneficial.
Full Page Cache (FPC) Impact
How Varnish Responds Differently to Separate Keys
Each unique cache key creates a different version in Varnish.
- Cache key A (guest) → one cached page
- Cache key B (affiliate) → another cached page
This way affiliates and guest users get their own versions stored. However, creating too many variations can lead to filling up cache storage fast and your hit rates drop.
Here’s how you can avoid cache bloat:
Use Magento’s cache reports or varnishstat to check your cache performance regularly.
Create variations only when it’s absolutely necessary.
Stick to simple values or booleans like user type or country instead of using things like timestamps and user IDs.
You need to cap variations. For example, if you have 3 locations x 2 devices x 2 flags, you’ve got a total of 12 versions, which is probably more than what you need.
Best Practices
You need to follow some best practices to keep your cache strategy up and running.
Keep Context Keys Minimal and Deterministic
- Leverage booleans or small enums (for example, guest|customer, mobile|desktop).
- Try to steer clear of anything that changes on each request.
Document Which Flags Impact Cache
- Maintain a Confluence page or README.md that lists the following:
- Each custom context
- Where the custom contexts are injected
- Which pages or blocks consume them
- Along with your code, version-control these docs as well.
Monitor Varnish Hit/Miss Ratio When Deploying Changes
- To make sure that the new contexts haven’t hurt the performance, schedule a quick varnishstat check after deploying the changes.
- In case there’s a drop of more than 5-10% in cache hit rate, remove unnecessary contexts or roll back the changes.
Conclusion
A successful store should have performance and personalization working side-by-side. By extending Http\Context, adding custom flags to your cache keys, and knowing when to turn back to JavaScript-driven private content, you’ll be able to deliver flawless personalized experiences minus any cache overloads. Remember, the two golden rules of contexts – less is more and track early, track often. Now, apply what you learned in this blog and take your Magento store to new heights.