Why product labels break under full page cache, and how OpenLabel avoids it
Wrong labels for logged-in customers, flicker after page load, slow category pages. Most label bugs on Magento come from one design choice. Here is what goes wrong and the approach OpenLabel takes instead.

Search the Magento forums for “product labels” and a pattern shows up. Labels flicker in after the page loads. A wholesale customer sees the retail “Sale” badge. A label that should have ended last night is still there this morning. Category pages get slower every time someone adds a label.
These look like separate bugs, but most of them come from the same question: where does the label get decided, and does the page cache know about it?
The three usual shortcuts
1. Load labels with AJAX after the page renders. The cached HTML has no labels, and JavaScript fetches them per product. It is always correct for the visitor, but every page view now costs an extra request, labels pop in late and push content around, and the result counts against Core Web Vitals.
2. Mark the label block as not cacheable. In Magento, a single non-cacheable block makes the whole page uncacheable. Labels become correct, and the shop becomes slow.
3. Evaluate rules for every product while rendering. The labels are in the cached page, which is good, but a cache miss on a category page now runs the rule conditions for every product, and often for every child of every configurable product. With a few labels and 36 products per page, that adds up quickly.
What OpenLabel does instead
Decide ahead of time
OpenLabel has its own indexer. When a label is saved, or when a product’s price, stock or reviews change, the indexer evaluates the label’s conditions in SQL and writes the result to an index table: which label applies to which product, in which store view, and for which customer group. Configurable, grouped and bundle parents get their own rows when a child matches, so nothing has to look at children while a page renders.
When a category page renders, OpenLabel collects the products on it and asks the index for all their labels in one query. That is the whole cost: one extra query for a 36-product listing, and one for a product page. The budget is checked in CI.
Let the page cache do what it already does
Magento’s full page cache and Varnish already vary a page by customer group. That is how a wholesale customer sees their own prices from cache. OpenLabel relies on the same mechanism. A label limited to one customer group is rendered on the server like any other label, and the cache simply holds one copy of the page per visiting group.
No AJAX, no private content sections and no JavaScript are needed to show the right label to the right customer.
Purge precisely
Every page that shows a label is tagged with that label and its design. Saving a label or a design purges exactly those pages, plus the product cards Hyvä caches for an hour. A scheduled label that starts or ends is handled by an hourly job, so it appears or disappears within the hour without a full reindex.
Position with CSS, not JavaScript
Labels are absolutely positioned inside the image container by one small generated stylesheet per store view. There are no inline styles, so the storefront stays CSP-clean, and nothing moves after the first paint. Layout shift with and without the module is the same.
The trade-off
This approach costs some work up front. The indexer has to run, and the index table grows with labels × products × store views, with one row per customer group for price-based labels. For catalogs in the tens of thousands of products, the full reindex is measured in CI against a 2,000-product budget test, and day-to-day changes are partial reindexes of only the products that changed.
In return, the storefront does almost nothing per request. That is the right place for the cost to be.
Try it
OpenLabel is free and MIT licensed. Install it with three Composer commands and click the “Sale -{SAVE_PERCENT}%” starter in Catalog › OpenLabel. The details of the cache behaviour are in the storefront documentation.