Performance is one of the most critical aspects of an ecommerce website. Having a high website load time directly affects your conversions and, hence, the revenue. Thankfully, for  Adobe Commerce (formerly Magento Commerce) and Magento Open Source stores, the tools inherently include Full Page Cache (FPC) to improve page load times. However, while FPC speeds up your site, it also makes it harder to show dynamic or personalized content. 

Luckily, there’s a way to handle it. This guide shows you how to serve dynamic content in Magento 2 or Adobe Commerce without breaking FPC, so you keep both speed and personalization.

What is Full Page Cache

Full Page Cache stores the fully rendered HTML of a page, allowing it to be served instantly without reprocessing layout XML, blocks, or templates. This significantly reduces server load and improves time-to-first-byte (TTFB).

Key benefits of this approach include:

  • Faster page load times
  • Reduced CPU usage
  • Lower TTFB (Time to First Byte)
  • Scalability under high traffic

However, FPC has a limitation that it serves the same cached content to all users, which can be an issue for dynamic, user-specific content.

The Challenge with Dynamic Content

If you inject dynamic data (like a customer name or cart items) directly into templates or blocks, you risk:

  • Disabling the cache (slower performance)
  • Serving cached personal data to other users (security risk)

To avoid this, dynamic content must be handled on the client side, using JavaScript and Magento’s private content approach.

How to Serve Dynamic Content without Breaking FPC

Follow this step-by-step guide to safely implement dynamic content on the product details page, all without compromising Full Page Cache performance:

Step 1: Create a Custom Module

Start by creating a Magento 2 module where you’ll manage your dynamic content logic.

Example: Klizer/DynamicContent

Set up the module with the required registration.php, composer.json, and module.xml.

app/code/Klizer/DynamicContent/registration.php

<?php
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Klizer_DynamicContent',
    __DIR__
);

app/code/Klizer/DynamicContent/composer.json

{
    "name": "klizer/module-dynamic-content",
    "description": "Klizer Dynamic Content Module for Magento 2",
    "require": {
        "php": "^7.4 || ^8.1",
        "magento/framework": "*"
    },
    "type": "magento2-module",
    "version": "1.0.0",
    "license": [
        "OSL-3.0",
        "AFL-3.0"
    ],
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Klizer\\DynamicContent\\": ""
        }
    }
}

app/code/Klizer/DynamicContent/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Klizer_DynamicContent" setup_version="1.0.0"/>
</config>

Step 2: Create a REST API Endpoint for Dynamic Data

Use a custom controller or service contract to expose the dynamic data (e.g., customer messages, offers, etc.)

app/code/Klizer/DynamicContent/etc/webapi.xml

<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi/etc/webapi.xsd">

    <route url="/V1/dynamic_data" method="POST">
        <service class="Klizer\DynamicContent\Api\DynamicDataInterface" method="get"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>

</routes>

app/code/Klizer/DynamicContent/Api/DynamicDataInterface.php

<?php

namespace Klizer\DynamicContent\Api;

interface DynamicDataInterface
{
    /**
     * GET
     * @return string
     */
    public function get();
}

app/code/Klizer/DynamicContent/Model/DynamicData.php

This method (get) is a placeholder implementation intended for testing dynamic content delivery. It currently returns the current GMT date and time as a simple example of dynamic output.

<?php

namespace Klizer\DynamicContent\Model;

use Klizer\DynamicContent\Api\DynamicDataInterface;

class DynamicData implements DynamicDataInterface
{

    public function get()
    {
        // to do
        // implement own logic here
        $timestamp = time();
        $currentDate = gmdate('m-d-Y h:i:s', $timestamp);

        return $currentDate;
    }
}
  • time() fetches the current Unix timestamp.
  • gmdate() formats the timestamp into a readable date-time string (e.g., 04-22-2025 03:45:12).
  • The result simulates dynamic content that changes with each request.

Note: You can replace this logic with any custom implementation, such as fetching user-specific data, personalized messages, cart info, or any other dynamic content required for your frontend.

app/code/Klizer/DynamicContent/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <preference for="Klizer\DynamicContent\Api\DynamicDataInterface"
                type="Klizer\DynamicContent\Model\DynamicData" />

</config>

Step 3: Add a Layout and JavaScript Component

Inject a container on the frontend where the dynamic data will appear. Use a layout XML file and a small Knockout.js or JavaScript component to fetch and render data asynchronously.

app/code/Klizer/DynamicContent/view/frontend/layout/catalog_product_view.xml

<?xml version="1.0"?>
<page
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="product.info.main">
            <block name="klizer-dynamic-content-block-pdp-page"
                   template="Klizer_DynamicContent::pdp_dynamic_content.phtml"
                   after="product-info-price"></block>

        </referenceContainer>
    </body>
</page>

app/code/Klizer/DynamicContent/view/frontend/templates/pdp_dynamic_content.phtml

<div class="dynamic-content-block">
    <p>Dynamic content is here</p>
    <div class="content-wrapper">
        <div id="replace-block-with-dynamic-content"> </div>
        </div>
    </div>


<script type="application/javascript">
    require(['jquery'],function ($){

        var api_url="http://magento247.local/rest/V1/dynamic_data/";
        $.post(api_url).done(function (response) {
            console.log(response);
            $('#replace-block-with-dynamic-content').replaceWith(response);
        }).fail(function (jqXHR, textStatus, err){

        })
    })

</script>

<style>
    .dynamic-content-block {
        font-size: 1.5rem;
        padding: 1rem;
        border: 1px solid #ccc;
        border-radius: 8px;
    }

    .dynamic-content-block span {
        font-weight: bold;
        font-size: 1.75rem;
        display: block;
        margin-bottom: 0.5rem;
    }

    .dynamic-content-block .content-wrapper {
        font-size: 2.5rem;
        color: #555;
    }
</style>

Step 4: Test It

Deploy the module, flush the cache, and test:

  • Load the page with FPC enabled
  • Confirm the dynamic message loads after the page render
  • Verify that the core HTML is still cached (X-Magento-Cache-Debug: HIT in headers)

Final Thoughts

Magento 2’s Full Page Cache is a powerful performance feature that needs to be used carefully when dynamic content is involved. You can personalize the customer experience without sacrificing speed or security by offloading dynamic rendering to the frontend via AJAX.

Use this pattern whenever you need to add user-specific content on a cached page, and you’ll get the best of both worlds: speed and flexibility.

And if you are looking for more expert help for Adobe Commerce development, our team will be happy to assist you. Book a free consultation now!

Frequently Asked Questions

  1. What’s the difference between Varnish and Fastly in Adobe Commerce?

Varnish is an open-source caching reverse proxy that Magento supports out of the box for Full Page Cache. Fastly is a cloud-based CDN and edge caching solution built on Varnish but optimized for global delivery, better edge logic, and additional features like image optimization, DDoS protection, and WAF. Adobe Commerce Cloud includes Fastly by default, making it ideal for high-scale production environments.

2. How do I debug or verify if Full Page Cache is working correctly?

You can check the FPC status using browser developer tools. Look for the response header X-Magento-Cache-Debug. A value of HIT means the page was served from cache, while MISS indicates it was generated dynamically. Magento’s developer toolbar and Varnish logs provide useful insights.

3. Why can’t I just render dynamic content directly in the .phtml file?

Rendering user-specific content directly in .phtml files or blocks will cause Magento to bypass the cache or, worse, cache personalized data for all users. This breaks Full Page Cache and can lead to serious performance and security issues. Instead, serve dynamic content via JavaScript and REST APIs.

4. What happens if I accidentally disable cache on a frequently visited page?

Disabling cache on high-traffic pages can significantly impact site performance. It increases server load, response time, and infrastructure costs. Always profile and test changes to ensure FPC remains intact, and offload dynamic content via frontend strategies.

Picture of Prabhakaran R

Prabhakaran R

Prabhakaran is a Senior Software Engineer and Magento Developer at DCKAP, known for his technical expertise and passion for exploring new technologies. With a dedication to continuous learning and a commitment to delivering exceptional solutions, he thrives in exploring the latest advancements in the field.
Scroll to Top