Google Tag Manager Setup for Magento | Blue Frog Docs

Google Tag Manager Setup for Magento

Complete guide to implementing Google Tag Manager on Magento 2 and Adobe Commerce, including module installation, manual implementation, and Varnish compatibility.

Google Tag Manager Setup for Magento

Implement Google Tag Manager (GTM) on your Magento 2 or Adobe Commerce store to manage analytics tags, tracking pixels, and marketing scripts without code deployments. This guide covers multiple implementation methods optimized for Magento's architecture.


Why Use GTM with Magento?

Benefits

1. Tag Management Without Code Changes

  • Deploy and update tags via GTM interface
  • No Magento code modifications required
  • Faster implementation of marketing tools

2. Performance Optimization

  • Async tag loading
  • Tag firing rules and conditions
  • Reduced server-side load

3. Team Collaboration

  • Marketing teams manage tags independently
  • Version control and rollback
  • Built-in debugging tools

4. Advanced Tracking


Implementation Methods

1. Google Tag Manager by Mageplaza

  • Features: Full GTM + GA4 integration, data layer, server-side GTM
  • Compatibility: Magento 2.3.x - 2.4.x
  • Price: Free / Pro version available

Installation:

composer require mageplaza/module-google-tag-manager
php bin/magento module:enable Mageplaza_Core Mageplaza_GoogleTagManager
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy -f
php bin/magento cache:flush

Configuration:

Stores > Configuration > Mageplaza > Google Tag Manager
  • Enable: Yes
  • GTM Container ID: GTM-XXXXXXX
  • Data Layer: Enable
  • Enhanced Ecommerce: Enable

2. Google Tag Manager Pro by MageWorx

  • Features: Advanced data layer, product impressions, user tracking
  • Installation: Via Magento Marketplace

3. GTM Integration by Amasty

  • Features: Full eCommerce tracking, custom events
  • Installation: Via Composer or Marketplace

Method 2: Custom Module Implementation

Build a custom GTM module for complete control.

Module Structure

app/code/YourCompany/GoogleTagManager/
├── etc/
│   ├── module.xml
│   ├── config.xml
│   ├── adminhtml/
│   │   └── system.xml
│   └── frontend/
│       └── di.xml
├── Block/
│   └── Gtm.php
├── Helper/
│   └── Data.php
└── view/frontend/
    ├── layout/
    │   └── default.xml
    └── templates/
        ├── head.phtml
        └── body.phtml

Step 1: Module Registration

File: registration.php

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

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

Step 2: Module Configuration

File: 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="YourCompany_GoogleTagManager" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Theme"/>
            <module name="Magento_Store"/>
        </sequence>
    </module>
</config>

Step 3: Admin Configuration

File: etc/adminhtml/system.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="yourcompany" translate="label" sortOrder="100">
            <label>Your Company</label>
        </tab>
        <section id="google_tag_manager" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Google Tag Manager</label>
            <tab>yourcompany</tab>
            <resource>YourCompany_GoogleTagManager::config</resource>
            <group id="general" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>General Settings</label>
                <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enable GTM</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="container_id" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>GTM Container ID</label>
                    <comment>Format: GTM-XXXXXXX</comment>
                    <validate>required-entry</validate>
                    <depends>
                        <field id="enabled">1</field>
                    </depends>
                </field>
                <field id="enable_datalayer" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enable Data Layer</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                    <depends>
                        <field id="enabled">1</field>
                    </depends>
                </field>
            </group>
        </section>
    </system>
</config>

Step 4: Helper Class

File: Helper/Data.php

<?php
namespace YourCompany\GoogleTagManager\Helper;

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Store\Model\ScopeInterface;

class Data extends AbstractHelper
{
    const XML_PATH_ENABLED = 'google_tag_manager/general/enabled';
    const XML_PATH_CONTAINER_ID = 'google_tag_manager/general/container_id';
    const XML_PATH_ENABLE_DATALAYER = 'google_tag_manager/general/enable_datalayer';

    public function isEnabled()
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_ENABLED,
            ScopeInterface::SCOPE_STORE
        );
    }

    public function getContainerId()
    {
        return $this->scopeConfig->getValue(
            self::XML_PATH_CONTAINER_ID,
            ScopeInterface::SCOPE_STORE
        );
    }

    public function isDataLayerEnabled()
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_ENABLE_DATALAYER,
            ScopeInterface::SCOPE_STORE
        );
    }
}

Step 5: Block Class

File: Block/Gtm.php

<?php
namespace YourCompany\GoogleTagManager\Block;

use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use YourCompany\GoogleTagManager\Helper\Data as GtmHelper;

class Gtm extends Template
{
    protected $gtmHelper;

    public function __construct(
        Context $context,
        GtmHelper $gtmHelper,
        array $data = []
    ) {
        $this->gtmHelper = $gtmHelper;
        parent::__construct($context, $data);
    }

    public function isEnabled()
    {
        return $this->gtmHelper->isEnabled();
    }

    public function getContainerId()
    {
        return $this->gtmHelper->getContainerId();
    }

    public function isDataLayerEnabled()
    {
        return $this->gtmHelper->isDataLayerEnabled();
    }

    protected function _toHtml()
    {
        if (!$this->isEnabled() || !$this->getContainerId()) {
            return '';
        }
        return parent::_toHtml();
    }
}

Step 6: Layout XML

File: view/frontend/layout/default.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">
    <head>
        <block class="YourCompany\GoogleTagManager\Block\Gtm"
               name="gtm.head"
               template="YourCompany_GoogleTagManager::head.phtml"
               before="-"/>
    </head>
    <body>
        <block class="YourCompany\GoogleTagManager\Block\Gtm"
               name="gtm.body"
               template="YourCompany_GoogleTagManager::body.phtml"
               before="-"/>
    </body>
</page>

Step 7: Head Template

File: view/frontend/templates/head.phtml

<?php
/** @var \YourCompany\GoogleTagManager\Block\Gtm $block */
$containerId = $block->escapeHtml($block->getContainerId());
?>
<!-- Google Tag Manager -->
<script>
    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','<?= $containerId ?>');
</script>
<!-- End Google Tag Manager -->

<?php if ($block->isDataLayerEnabled()): ?>
<script>
    // Initialize data layer
    window.dataLayer = window.dataLayer || [];
</script>
<?php endif; ?>

Step 8: Body Template

File: view/frontend/templates/body.phtml

<?php
/** @var \YourCompany\GoogleTagManager\Block\Gtm $block */
$containerId = $block->escapeHtml($block->getContainerId());
?>
<!-- Google Tag Manager (noscript) -->
<noscript>
    <iframe src="https://www.googletagmanager.com/ns.html?id=<?= $containerId ?>"
            height="0" width="0" style="display:none;visibility:hidden"></iframe>
</noscript>
<!-- End Google Tag Manager (noscript) -->

Step 9: Enable Module

php bin/magento module:enable YourCompany_GoogleTagManager
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy -f
php bin/magento cache:flush

Method 3: Manual Implementation via Admin Panel

Quick implementation without module development.

Add GTM to Head

  1. Navigate to:

    Content > Design > Configuration
    
  2. Select Store View and click Edit

  3. Expand HTML Head section

  4. Add to Scripts and Style Sheets:

    <script>
    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXXXX');
    </script>
    

Add GTM to Body

  1. Expand Footer section

  2. Add to Miscellaneous HTML:

    <noscript>
    <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
            height="0" width="0" style="display:none;visibility:hidden"></iframe>
    </noscript>
    
  3. Save Configuration

  4. Flush Cache:

    php bin/magento cache:flush
    

Limitations:

  • No dynamic data layer integration
  • Manual updates required for container ID changes
  • Limited access to Magento session/customer data

Method 4: Theme-Level Implementation

Implement GTM at theme level for reusability.

File: app/design/frontend/Vendor/theme/Magento_Theme/layout/default.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">
    <head>
        <block class="Magento\Framework\View\Element\Template"
               name="gtm_head"
               template="Magento_Theme::gtm-head.phtml"
               before="-"/>
    </head>
    <body>
        <block class="Magento\Framework\View\Element\Template"
               name="gtm_body"
               template="Magento_Theme::gtm-body.phtml"
               before="-"/>
    </body>
</page>

Template: app/design/frontend/Vendor/theme/Magento_Theme/templates/gtm-head.phtml

<!-- Google Tag Manager -->
<script>
    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXXXX');
</script>
<!-- End Google Tag Manager -->

Server-Side GTM Integration

For improved accuracy and performance, implement server-side GTM.

Server-Side GTM Architecture

Magento Store → GTM Server-Side → GA4/Ads/Pixels

Benefits

  • Better data accuracy (no ad blockers)
  • Enhanced security (sensitive data stays server-side)
  • Improved page performance
  • Better control over data sent to third parties

Implementation Steps

1. Set Up GTM Server Container

  • Create Server container in GTM
  • Deploy to Google Cloud or Tagging Server

2. Configure Server Endpoint

File: app/code/YourCompany/GoogleTagManager/Model/ServerSide.php

<?php
namespace YourCompany\GoogleTagManager\Model;

use Magento\Framework\HTTP\Client\Curl;

class ServerSide
{
    protected $curl;
    protected $helper;

    public function __construct(
        Curl $curl,
        \YourCompany\GoogleTagManager\Helper\Data $helper
    ) {
        $this->curl = $curl;
        $this->helper = $helper;
    }

    public function sendEvent($eventName, $eventData)
    {
        $serverUrl = $this->helper->getServerSideUrl();

        if (!$serverUrl) {
            return false;
        }

        $payload = [
            'event_name' => $eventName,
            'client_id' => $this->getClientId(),
            'user_data' => $this->getUserData(),
            'event_data' => $eventData
        ];

        $this->curl->setOption(CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        $this->curl->post($serverUrl, json_encode($payload));

        return $this->curl->getStatus() === 200;
    }

    protected function getClientId()
    {
        // Get from cookie or generate
        return $_COOKIE['_ga'] ?? $this->generateClientId();
    }

    protected function getUserData()
    {
        // Return user data for enhanced conversions
        return [];
    }

    protected function generateClientId()
    {
        return sprintf('%04x%04x.%04x%04x',
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),
            mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }
}

Full Page Cache & Varnish Compatibility

Ensure GTM works with Magento's caching layers.

Private Content Sections

For dynamic GTM data with FPC enabled:

File: etc/frontend/sections.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer/etc/sections.xsd">
    <action name="catalog/product/view">
        <section name="gtm-data"/>
    </action>
    <action name="checkout/cart/add">
        <section name="gtm-data"/>
    </action>
</config>

ESI Tags for Varnish

File: view/frontend/layout/default.xml

<block class="YourCompany\GoogleTagManager\Block\Gtm"
       name="gtm.datalayer"
       template="YourCompany_GoogleTagManager::datalayer.phtml">
    <arguments>
        <argument name="ttl" xsi:type="number">0</argument>
    </arguments>
</block>

RequireJS Customer Data

Load dynamic data via customer sections:

Template:

<script>
    require(['Magento_Customer/js/customer-data'], function(customerData) {
        var gtmData = customerData.get('gtm-data');

        gtmData.subscribe(function(data) {
            if (data.events) {
                data.events.forEach(function(event) {
                    window.dataLayer.push(event);
                });
            }
        });
    });
</script>

GTM Configuration

Container Setup

  1. Create Container:

    • Go to tagmanager.google.com
    • Click Create Account
    • Enter account and container details
    • Select Web as target platform
  2. Add Workspace:

    • Default workspace created automatically
    • Add staging workspace for testing

Basic Tags

1. Google Analytics 4 Configuration Tag

  • Tag Type: Google Analytics: GA4 Configuration
  • Measurement ID: G-XXXXXXXXXX
  • Trigger: All Pages

2. Google Analytics 4 Event Tag

  • Tag Type: Google Analytics: GA4 Event
  • Configuration Tag: [Select GA4 Config Tag]
  • Event Name: \{\{Event\}\} (variable)
  • Trigger: Custom Event

Testing & Validation

GTM Preview Mode

  1. Open GTM container
  2. Click Preview
  3. Enter Magento store URL
  4. Debug tags in Tag Assistant

Browser Console

Check data layer:

console.log(window.dataLayer);

Network Tab

Verify GTM requests:

  1. Open DevTools > Network
  2. Filter by googletagmanager.com
  3. Check for gtm.js and collect requests

Tag Assistant

  1. Install Tag Assistant Chrome Extension
  2. Visit Magento store
  3. Verify GTM tag fires correctly

Performance Optimization

DNS Prefetch

Add to layout:

<link rel="dns-prefetch" href="//www.googletagmanager.com"/>

Async Loading

GTM script loads asynchronously by default:

j.async=true;

Script Minification

Enable in Magento:

php bin/magento config:set dev/js/minify_files 1
php bin/magento config:set dev/js/merge_files 1

Security Considerations

Content Security Policy (CSP)

Add GTM to CSP whitelist:

File: etc/csp_whitelist.xml

<?xml version="1.0"?>
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd">
    <policies>
        <policy id="script-src">
            <values>
                <value id="gtm" type="host">*.googletagmanager.com</value>
            </values>
        </policy>
        <policy id="img-src">
            <values>
                <value id="gtm_img" type="host">*.googletagmanager.com</value>
            </values>
        </policy>
        <policy id="connect-src">
            <values>
                <value id="gtm_connect" type="host">*.googletagmanager.com</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>

Troubleshooting

GTM Container Not Loading

Solutions:

  1. Verify container ID format: GTM-XXXXXXX
  2. Check browser console for errors
  3. Disable ad blockers
  4. Clear Magento cache: php bin/magento cache:flush

Tags Not Firing

Solutions:

  1. Use GTM Preview mode for debugging
  2. Check trigger configurations
  3. Verify data layer variables exist
  4. Review tag firing order

Data Layer Not Populating

Solutions:

  1. Check JavaScript console for errors
  2. Verify RequireJS dependencies loaded
  3. Ensure customer sections are enabled
  4. Check FPC/Varnish configuration

Next Steps


Additional Resources

// SYS.FOOTER