Customer Segments Splitting Magento’s Full Page Cache

Adobe Commerce customer segments go into the full page cache key and split it for logged-in shoppers. How it happens and the patch we use.

Full page cache is the single biggest performance feature in Magento. It only works if many visitors share the same cached copy of a page. Anything that splits visitors into more groups means more copies, fewer cache hits, and more requests rendered from scratch.

On Adobe Commerce, customer segments are one of those splitters, and it is easy to miss. This post explains how segments end up in the cache key, what that costs, and the small patch we use to stop it on stores that do not need segment-specific page content.

One web page copied into separate stacks for different groups of visitors

How Magento Decides Which Cached Copy to Serve

Magento keeps a set of values called the HTTP context for every visitor: things like the store view, the currency and the customer group. Any value that differs from the default goes into a hash, which Magento stores in the X-Magento-Vary cookie. The full page cache, whether built-in, Varnish or Fastly, uses that hash as part of the cache key. Two visitors with different hashes never share a cached page.

That is the right design for values that change what the page shows, such as currency. The problem starts when values get into the context that most pages do not use.

Where Customer Segments Come In

The Customer Segment module in Adobe Commerce adds a plugin that runs before every frontend action. For a logged-in customer, it looks up all the segments the customer belongs to and writes their IDs into the HTTP context. For guests it writes an empty list, which matches the default and changes nothing.

So every logged-in customer gets a cache key that includes their exact set of segments. Customers in different combinations of segments get different copies of the same category page, the same product page and the same home page, even when nothing on those pages depends on segments.

On the store where we patched this, there were only three active segments. That still means up to eight possible combinations, each with its own copy of every page, each warmed separately by the first logged-in visitor who happens to have that combination. Logged-in shoppers are often the most valuable traffic on the site, and they were the ones least likely to hit the cache. Stores with more segments split the cache further.

With three customer segments one page had up to nine cached copies; without segments in the cache key only guest and logged-in

The Fix: Keep Segments Out of the HTTP Context

Our patch adds one plugin to the Customer Segment module. It runs after Magento\Framework\App\Http\Context::setValue(). When the value being set is the customer segment one, it removes it again:

public function afterSetValue(Subject $httpContext, Subject $result, string $name): Subject
{
    if (Data::CONTEXT_SEGMENT === $name) {
        $httpContext->unsValue($name);
    }

    return $result;
}

Segment membership is still calculated, and everything that reads it from the customer session or the database keeps working. It simply no longer splits the full page cache.

When Not to Use It

This patch is only safe if nothing in your cached pages changes by segment. Check before you apply it:

  • Segment-targeted content blocks and banners. If you show different dynamic blocks or banners to different segments in cached HTML, the first visitor’s version will be served to everyone.
  • Cart price rules by segment are fine. Discounts are calculated in the cart and at checkout, which are not served from the full page cache.
  • Private content is fine. Anything loaded per customer through customer sections, such as the mini cart or the customer name, never comes from the page cache in the first place.

If you do need segment-specific content on cached pages, a better route is to load that one block through a private content section or an AJAX call, and keep the rest of the page shared.

How to Check Your Own Store

  • Log in as two customers in different segments and compare the X-Magento-Vary cookie. If it differs while store, currency and customer group are the same, segments are in your cache key.
  • Look at the full page cache hit rate for logged-in visitors separately from guests. A large gap is a sign that something in the HTTP context is splitting them.
  • List every module that calls Http\Context::setValue(). Segments are one example; any third-party module can add its own values.

This is one entry in our collection of Magento performance patches. Cache hit rate is one of the first things we look at in Magento support work, because it decides how much of your traffic Magento has to render at all.

Download the Patch

The patch targets magento/module-customer-segment 102.1.8-p3, which ships with Adobe Commerce 2.4.8. It is provided as-is: read it, check the conditions above, and test it on staging before production.

magento-customer-segment-keep-segments-out-of-page-cache.patch on GitHub · raw file

All our patches, with a short guide to applying them, are in the paxento/magento-patches repository on GitHub.

The full diff:

diff --git a/Model/App/Http/ContextPlugin.php b/Model/App/Http/ContextPlugin.php
new file mode 100644
index 0000000..d91ed50
--- /dev/null
+++ b/Model/App/Http/ContextPlugin.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Magento\CustomerSegment\Model\App\Http;
+
+use Magento\CustomerSegment\Helper\Data;
+use Magento\Framework\App\Http\Context as Subject;
+
+class ContextPlugin
+{
+    /**
+     * Unset customer segment ids from HTTP context
+     *
+     * @param Subject $httpContext
+     * @param Subject $result
+     * @param string $name
+     * @return Subject
+     */
+    public function afterSetValue(
+        Subject $httpContext,
+        Subject $result,
+        string $name
+    ): Subject {
+        if (Data::CONTEXT_SEGMENT === $name) {
+            $httpContext->unsValue($name);
+        }
+
+        return $result;
+    }
+}
diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml
index 5b4794e..ec64bde 100644
--- a/etc/frontend/di.xml
+++ b/etc/frontend/di.xml
@@ -14,6 +14,10 @@
         <plugin name="customer-segment-app-action-dispatchController-context-plugin"
                 type="Magento\CustomerSegment\Model\App\Action\ContextPlugin" sortOrder="10"/>
     </type>
+    <type name="Magento\Framework\App\Http\Context">
+        <plugin name="customer-segment-app-http-context-plugin"
+                type="Magento\CustomerSegment\Model\App\Http\ContextPlugin" sortOrder="10"/>
+    </type>
     <type name="Magento\Checkout\Model\Cart\CollectQuote">
         <plugin name="checkout_cart_collect_totals" type="Magento\CustomerSegment\Model\Checkout\Block\Cart\Shipping\Plugin"/>
     </type>

Siarhei Pankevich Avatar

Founder & CTO, Paxento

Discover more from Paxento

Subscribe now to keep reading and get access to the full archive.

Continue reading