DEV Community

Magevanta
Magevanta

Posted on • Originally published at magevanta.com

Magento 2 Cache Tag Strategy: Prevent Cache Invalidation Storms

Magento 2's full page cache is one of its strongest performance features — when it works. But every week, we see stores where a simple product save triggers a 30-second Varnish flush and subsequent cache stampede. The culprit is almost never Varnish itself. It's cache tags.

This post covers how Magento 2 cache tags work, why broad tags destroy performance, and exactly how to audit and fix them.

How Cache Tags Work in Magento 2

Every cached page, block, and data fragment in Magento is tagged with identifiers. When a product changes, Magento invalidates all cache entries tagged with that product's ID. The tag system is hierarchical:

  • cat_p_123 — specific product
  • cat_p — all products
  • cat_c_5 — specific category
  • cat_c — all categories
  • cms_b_about_us — a CMS block
  • cms_p — all CMS pages

These tags are stored alongside cached content and used during invalidation. When you call $cache->clean(["cat_p_123"]), every cache entry tagged with cat_p_123 is removed. This is elegant until someone tags a global block with cat_p, and saving any product flushes half your store.

The Invalidation Storm Problem

Here's what happens during a storm:

  1. Admin saves a simple product update (price change)
  2. Magento generates the invalidation list: cat_p_456, cat_c (because the product is in categories), cat_p (from a badly written block)
  3. cat_p is too broad — it matches the product list page, layered navigation, homepage widgets, and every product detail page
  4. Varnish receives 50,000 BAN requests
  5. Store goes from sub-100ms response times to 2-5 seconds for the next 10 minutes while the cache rebuilds

We've seen this on a store with 80,000 SKUs. A single product save dropped cache hit rate from 94% to 12%.

Diagnosing Bad Cache Tags

Check Your Current Tags

Add this to any block template to inspect what tags are being applied:

$block->getCacheKeyInfo();
// Or for the full page:
$block->getIdentities();
Enter fullscreen mode Exit fullscreen mode

For a full audit, intercept cache writes in development:

// In di.xml:
<type name="Magento\Framework\App\Cache\Type\Layout">
    <plugin name="tag_audit" type="Vendor\Module\Plugin\CacheTagAudit"/>
</type>

// In CacheTagAudit.php:
public function beforeSave($subject, $data, $id, $tags = [], $lifeTime = null)
{
    if (in_array('cat_p', $tags) || in_array('cat_c', $tags)) {
        $this->logger->warning("Broad cache tag detected", [
            'tags' => $tags,
            'id' => $id
        ]);
    }
    return [$data, $id, $tags, $lifeTime];
}
Enter fullscreen mode Exit fullscreen mode

Monitor Varnish BANs

Watch your Varnish logs during a product save. If you see hundreds of BAN requests for a single save, you have a tag granularity problem:

varnishlog -g request -q 'ReqMethod == "BAN"' | head -50
Enter fullscreen mode Exit fullscreen mode

Profile with Blackfire

Use Blackfire's cache invalidation metric. A healthy store shows 5-20 invalidated entries per product save. If you see 5,000+, investigate immediately.

Common Sources of Broad Tags

1. Custom Blocks Without getIdentities()

When a block doesn't define getIdentities(), Magento falls back to broad defaults. Always implement it:

class CustomProductList extends \Magento\Framework\View\Element\Template
{
    public function getIdentities()
    {
        $identities = [\Magento\Catalog\Model\Product::CACHE_TAG];
        // ^ THIS IS THE PROBLEM — it adds 'cat_p' for every product

        foreach ($this->getProducts() as $product) {
            $identities[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $product->getId();
        }
        return array_unique($identities);
    }
}
Enter fullscreen mode Exit fullscreen mode

The fix: only tag with specific product IDs, never the global cat_p tag unless you genuinely want every product cache cleared.

2. CMS Blocks with Dynamic Content

A CMS block containing a product widget often gets tagged with cms_b only. When the underlying products change, the block doesn't invalidate. The knee-jerk fix is adding cat_p to the block's identities — which then causes storms. The correct approach is granular tagging:

public function getIdentities()
{
    $identities = [\Magento\Cms\Model\Block::CACHE_TAG . '_' . $this->getBlockId()];

    foreach ($this->getWidgetProducts() as $product) {
        $identities[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $product->getId();
    }

    return array_unique($identities);
}
Enter fullscreen mode Exit fullscreen mode

3. Third-Party Extensions

Many extensions add global tags carelessly. We've seen a popular review extension tag every page with cat_p because "products might have reviews." Audit every third-party module that touches caching:

  • Search for CACHE_TAG in the vendor directory
  • Look for blocks that return [$this->_cacheTag] without product-specific IDs
  • Check observers that call cleanType or clean with broad tags

4. Layered Navigation and Category Pages

Category pages are the most commonly over-tagged. A category with 500 products should be tagged with cat_c_5 plus each individual cat_p_XXX. But some implementations tag the entire page with just cat_p, meaning any product save anywhere invalidates every category page.

The correct tagging for a category page:

public function getIdentities()
{
    $identities = [\Magento\Catalog\Model\Category::CACHE_TAG . '_' . $this->getCategoryId()];

    foreach ($this->getLoadedProductCollection() as $product) {
        $identities[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $product->getId();
    }

    return array_unique($identities);
}
Enter fullscreen mode Exit fullscreen mode

This means saving product 456 only invalidates category pages where product 456 appears — not every category page in the store.

Fixing Tag Granularity: A Systematic Approach

Step 1: Map Your Cache Surface

Document what should invalidate what:

Cache Entry Should Invalidate On
Product detail page That product's save
Category page Category save + products in that category
Homepage CMS/homepage config changes
Cart/Checkout Quote changes
Search results Indexer run

If a product save invalidates your homepage, that's a bug.

Step 2: Fix Custom Modules

Go through your custom code and replace broad tags with specific ones:

// BAD — invalidates everything
return [\Magento\Catalog\Model\Product::CACHE_TAG];

// GOOD — only invalidates when this specific product changes
return [\Magento\Catalog\Model\Product::CACHE_TAG . '_' . $product->getId()];

// BEST — also include category-specific tags for list pages
return [
    \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $product->getId(),
    \Magento\Catalog\Model\Category::CACHE_TAG . '_' . $categoryId
];
Enter fullscreen mode Exit fullscreen mode

Step 3: Patch or Replace Bad Extensions

If a third-party extension uses broad tags, you have three options:

  1. Override via plugin — intercept getIdentities() and filter the tags
  2. Contact the vendor — legitimate performance bugs deserve patches
  3. Replace the extension — some are beyond saving

A plugin to strip dangerous tags:

public function afterGetIdentities($subject, $result)
{
    $forbidden = ['cat_p', 'cat_c', 'cms_p'];
    return array_diff($result, $forbidden);
}
Enter fullscreen mode Exit fullscreen mode

Use this carefully — only strip tags you know are wrong. Don't break legitimate invalidation.

Step 4: Add Cache Warming After Fixes

Once your tags are clean, you'll see fewer invalidations — but the ones that do happen will be more targeted. Set up cache warming for critical pages so that targeted invalidations don't leave cold cache entries:

# Warm the homepage and top categories after any product save
n98-magerun2.phar cache:warm --urls="/" --urls="/women.html" --urls="/men.html"
Enter fullscreen mode Exit fullscreen mode

Varnish-Specific Considerations

Varnish's BAN-based invalidation is extremely fast for small tag sets, but degrades with large ones. Each BAN request checks every cached object's tags. If you're sending 10,000 BANs, Varnish spends more time processing invalidations than serving requests.

Use Purge Instead of BAN When Possible

For specific object invalidation (single URL), use PURGE:

curl -X PURGE https://your-store.com/product-123.html \
  -H "X-Magento-Tags-Pattern: cat_p_123"
Enter fullscreen mode Exit fullscreen mode

This is more efficient than BAN for single-object invalidation.

Increase Varnish's Ban Lurker Speed

In varnish.vcl, tune the ban lurker:

sub vcl_init {
    new ban_lurker = debug.ban_lurker(
        interval = 0.1s,
        batch_size = 1000
    );
}
Enter fullscreen mode Exit fullscreen mode

This helps Varnish process pending BANs faster between requests.

Consider Soft Purge for Non-Critical Content

If you have content where serving stale data for 5 minutes is acceptable, use soft purge:

curl -X SOFTBAN https://your-store.com/
Enter fullscreen mode Exit fullscreen mode

This marks content as stale but doesn't immediately remove it — new requests trigger a background refresh.

Measuring the Impact

After fixing tags, measure these metrics over a week:

  1. Cache hit rate — should increase (target 90%+ for product pages)
  2. Varnish BAN count per product save — should drop from thousands to <50
  3. 90th percentile response time after product updates — should drop dramatically
  4. CPU usage during peak traffic — should decrease due to fewer cache rebuilds

Use this query to check your current tag distribution:

# Check how many unique tags Varnish is tracking
varnishstat -1 | grep n_ban
# Look for n_ban_add and n_ban_obj_test — high numbers indicate tag bloat
Enter fullscreen mode Exit fullscreen mode

Summary

Cache tag granularity is the silent performance killer in Magento 2. A single broad tag can turn a routine product update into a site-wide performance incident. The fix is systematic:

  1. Audit your cache tags in development
  2. Fix custom blocks to use specific product/category IDs
  3. Patch or replace third-party extensions with bad tag hygiene
  4. Monitor Varnish BAN counts in production
  5. Add cache warming for critical paths

Stores that fix their tag strategy typically see 40-60% reduction in post-save response time spikes and a 15-20% increase in overall cache hit rate. It's low-hanging fruit — but only if you know to look for it.

Top comments (0)