Cache Tag Strategy for Multi-Store Magento Setups With Redis
AI generated
SET
TTL
Redis / Magento Multi-Store
Cache Tag Strategy for Multi-Store Magento Setups
granular invalidation instead of a global flush

Magento's full page cache invalidates entries through tags like cat_p_123 or cat_c_45, which are entity-scoped but not store-specific by default. In a multi-store installation with several websites, store groups, and store views, that means a change actually relevant to only a single store view, such as a store-specific price rule or an individual approval status, can still invalidate cache entries for every other store because they share the same tag namespace. Anyone running many stores needs to deliberately decide how granular tags should be, without sliding into a tag explosion with an unmanageable number of barely reused tags.

12 min read Cache tags per store view Tag cardinality Invalidation-rate monitoring

1. Why global flush operations get expensive with many stores

In a single-store installation, classic tag invalidation is unproblematic: when a product changes, the associated tag gets cleared, and the affected, manageable part of the cache gets rebuilt. In an installation with ten or more store views across different domains or languages, the number of cache entries per entity multiplies accordingly, because every store view holds its own, store-specific HTML variant in the cache.

When a store-wide tag gets invalidated, every store variant suddenly loses its cache entry at once, even if the actual change was only relevant to a single store view, such as a store-specific price change from a website-bound promotion. The result is an unnecessarily high number of simultaneous cache misses and a noticeable load spike on the database and application servers when many visitors request freshly regenerated pages across stores in quick succession.

2. How Magento builds cache tags today

Magento's cache tags follow an entity-scoped scheme: a product produces a tag like cat_p_123, a category cat_c_45, regardless of how many store views or websites that entity is visible in. These tags get attached automatically to all affected cache entries whenever the entity is saved, through Magento's indexer and cache invalidation logic, while the actual store distinction only happens through the X-Magento-Vary header and the cache key derived from it, not through the tag itself.

This separation is deliberate: the tag identifies the entity, the cache key identifies the concrete, context-dependent variant. For most use cases that's sufficient, because a product change is usually relevant to all stores equally anyway. For store-specific attributes, individual prices, or website-bound visibility, however, this produces an unnecessarily coarse granularity for invalidation.

3. Implementing store-view-specific tag extensions

To invalidate more granularly, the existing tag can be extended with the store ID, for instance as an additional tag cat_p_123_store_2 alongside the classic, store-wide cat_p_123. When an entity is saved, the code then checks whether the change actually affects all assigned stores or only a subset, for instance because only a store-specific attribute such as a local price override changed. Only in the first case does the global tag get invalidated; in the second, only the affected store-specific tags do.

Technically this can be implemented via a plugin on the relevant blocks' cache tag generation, or through a dedicated observer on the save events of the given entity, which passes the store-specific variants to the invalidation logic in addition to the standard tags, without touching existing Magento core logic.


<?php
declare(strict_types=1);

namespace Mironsoft\StoreCacheTags\Plugin;

use Magento\Catalog\Model\Product;
use Magento\Framework\App\CacheInterface;

/**
 * Adds store-specific cache tags when a change provably affects
 * only individual store views.
 */
class AddStoreScopedTags
{
    /**
     * @param CacheInterface $cache Magento cache frontend, Redis-backed.
     */
    public function __construct(private readonly CacheInterface $cache)
    {
    }

    /**
     * Invalidates store-specific tags in addition to the global tag.
     *
     * @param Product $product Saved product entity.
     * @param array $affectedStoreIds Store IDs with an actual change.
     * @return void
     */
    public function invalidateScopedTags(Product $product, array $affectedStoreIds): void
    {
        $tags = [];
        foreach ($affectedStoreIds as $storeId) {
            $tags[] = sprintf('cat_p_%d_store_%d', $product->getId(), $storeId);
        }

        $this->cache->clean($tags);
    }
}

4. Avoiding tag explosion: the limit of granularity

The obvious mistake with this approach is pushing granularity too far: a tag per combination of entity, store view, and customer group would quickly produce five hundred tags per product with fifty stores and ten customer groups, most of which never get invalidated individually. Redis itself manages tags through set structures referencing every associated cache key, so an overly fine split doesn't just inflate the number of tags but also the memory footprint of those internal set structures unnecessarily.

A sensible rule of thumb is to align granularity with the actual change frequency: store-specific tags pay off where changes regularly affect only individual stores, such as localized prices or store-bound promotions. For attributes that are practically always changed store-wide, such as base data like SKU or weight, the simple global tag remains the better choice, since the extra granularity brings no real benefit there.

5. Selective flush instead of a global cache clean on deploy and maintenance

Besides ongoing invalidation from entity changes, multi-store setups often face a second problem: after a deploy or a configuration change, the entire cache frequently gets flushed reflexively via bin/magento cache:flush, even though the change only affected a single website or store group. With many stores, that means an unnecessarily broad cache cold start for areas that were never affected by the actual change at all.

With store-specific tags, a selective flush per website or store group can be implemented instead, for instance through a dedicated CLI command that collects all tags for a given store ID and removes only those via redis-cli or the Magento cache API, while the cache for every other store stays untouched and keeps serving hits.


# Find and delete all cache keys within a given store's tag namespace
STORE_ID=2
redis-cli --scan --pattern "*_store_${STORE_ID}*" | while read -r key; do
    redis-cli DEL "$key"
done

6. Monitoring the tag invalidation rate in production

Without measurement, it stays unclear whether a store-specific tag strategy actually achieves its intended effect. A counter that logs, for every invalidation, how many cache keys were actually affected and whether it was a global or a store-specific tag is worth having. If the number of globally invalidated keys doesn't drop noticeably despite introducing store-specific tags, that's a sign the distinction between store-wide and store-specific changes isn't being made correctly in practice.

In addition, INFO keyspace in Redis gives a rough overview of the total key count, while redis-cli --scan --pattern 'cat_p_*_store_*' | wc -l can determine the number of store-specific tag variants specifically, keeping cardinality and growth over time in view.


# Count store-specific tags versus global tags
GLOBAL=$(redis-cli --scan --pattern 'cat_p_*' | grep -cv '_store_')
SCOPED=$(redis-cli --scan --pattern 'cat_p_*_store_*' | wc -l)
echo "Global: $GLOBAL, Store-specific: $SCOPED

7. The extra dimension: customer-group-dependent prices

Store-specific tags solve the store problem but don't automatically cover price differences between customer groups within the same store view. If a special price rule changes only for a specific customer group, such as wholesale customers, that doesn't affect other stores, but it still affects every customer-group variant of the cache within that store view, unless additional differentiation is introduced.

In practice, an additional customer-group dimension in the tags usually pays off only for installations with very few, clearly separated customer groups and frequent, group-specific price changes. With many customer groups, the extra cardinality should be weighed against the actual benefit, since otherwise the same tag explosion seen with overly granular store tags repeats itself.

8. Testing the tag strategy under realistic load

Before going to production, a test run on a staging environment with a representative number of stores and simulated price changes is worth doing, specifically measuring how many cache keys per change actually get invalidated compared to the previous, purely global behavior. Only that allows an objective assessment of whether the extra complexity of store-specific tags actually achieves the hoped-for effect.

A simple test setup saves a product with a store-specific price change, measures the number of remaining, still-valid cache keys for every other store view before and after, and compares the result against a run without store-specific tags. If the number of unrelated, still-valid keys stays noticeably higher, that confirms the practical benefit of the more granular strategy.

9. Limits and trade-offs of the granular tag strategy

Every additional tag dimension increases the complexity of the invalidation code and therefore the risk that an overlooked case leads to stale, non-invalidated cache entries, which is harder to diagnose than a simply too-aggressive global invalidation. Every new extension should therefore be backed by automated tests that explicitly verify store-specific changes really only invalidate the affected stores and leave every other store untouched.

For small installations with few stores, the effort usually isn't worth it, because the difference between global and store-specific invalidation is barely noticeable in practice. Only from a double-digit number of store views with independently changing price or visibility rules does the extra granularity pay off through noticeably fewer unnecessary cache misses.

Approach Global tag Store-specific tag Practical relevance
Granularity One tag per entity One tag per entity and store Finer for store-specific changes
Invalidation scope All stores at once Only affected stores Fewer unnecessary cache misses
Cardinality Low, scales well Grows with store count Watch tag-explosion risk
Implementation effort Already present in Magento Additional plugin needed Introduce only where truly needed
Suited for Small installations Ten or more store views From noticeable change frequency onward

Mironsoft

Cache layer setup and Magento Redis integration

Magento cache that isn't quite working or is misconfigured?

We set up Redis as a cache and session backend for Magento cleanly, tune memory usage and eviction strategies, and make sure full page cache and session storage work together reliably.

Redis Setup

Configure the cache, session, and FPC backend production-ready for Magento.

Memory Tuning

Match memory usage and eviction policies to the shop's actual load.

High Availability Setup

Set up Redis Sentinel or Cluster for resilient Magento environments.

10. Summary

Redis Cache Tags for Multi-Store Magento: The Essentials at a Glance

Starting problem

Magento's cache tags are entity-scoped but store-wide, so store-specific changes unnecessarily invalidate many stores at once.

Solution approach

Store-specific tag variants like cat_p_123_store_2 complement the global tag and allow targeted, store-scoped invalidation.

Granularity limit

Too many tag dimensions lead to tag explosion; granularity should be aligned with actual change frequency.

Monitoring

Separate counters for global and store-specific invalidations, plus regular cardinality checks via redis-cli SCAN, reveal the actual benefit.

11. FAQ: Redis Cache Tags for Multi-Store Magento: The Essentials at a Glance

1Why aren't Magento's default cache tags store-specific?
Tags like cat_p_123 identify the entity regardless of store, while the actual store distinction happens through the X-Magento-Vary header and the cache key, not through the tag itself.
2What's the problem with a store-specific price change without store-specific tags?
Invalidation happens through the global tag and therefore affects all stores at once, even though the change actually only concerned a single store view.
3How is a store-specific tag concretely built?
As an additional tag with the store ID appended, such as cat_p_123_store_2, which only gets invalidated when a change provably affects only that store view.
4What is tag explosion?
An overly fine split, such as one tag per combination of entity, store, and customer group, produces a very high number of tags that unnecessarily inflates the memory footprint of Redis's set structures.
5How do you decide how granular tags should be?
Based on actual change frequency: store-specific tags pay off for frequently store-bound changes, while store-wide base data is well served by the simple global tag.
6What is a selective flush as opposed to cache:flush?
Instead of clearing the entire cache, only the cache keys of a specific store ID or website are removed in a targeted way, while the cache for every other store remains intact.
7How is the invalidation rate practically monitored?
Through a counter that logs global and store-specific invalidations separately, plus regular cardinality checks via redis-cli SCAN over the respective tag namespaces.
8Does a store-specific tag strategy also cover customer-group prices?
Not automatically. Group-specific price changes require an additional customer-group dimension in the tags, which only makes sense with few, clearly separated groups.
9How do you test whether the tag strategy actually works?
Through a comparison run on a staging environment that measures and contrasts the number of invalidated cache keys per change with and without store-specific tags.
10From how many stores does the extra effort pay off?
Usually only from a double-digit number of store views with independently changing price or visibility rules; for small installations, the difference is barely noticeable.