How to Add Google Tag Manager to Magento 2 (and Keep the Container Loading on Every Page)

|Dan Giura
How to Add Google Tag Manager to Magento 2 (and Keep the Container Loading on Every Page)

TL;DR

Installing the container is two paste operations. Keeping it loading is the part that breaks later, after a theme change, a JavaScript optimizer, or a Magento upgrade that tightens checkout. The extension renders the snippet you paste, which is why it belongs in its two fields and not in a theme template or the HTML Head setting. Magento's own script mover has been handled since v1.14.9. A third-party optimizer needs Script Attributes. Then you check one page of each type, success page last.

Key Takeaways

  • Google's placement rule: the first snippet as high in the <head> as possible, the second immediately after the opening <body> tag [2]. The extension's two fields put them there [1].
  • The snippet you paste is rendered by the extension, so it inherits the Move JS exemption and the CSP nonce. A second copy in Content > Design > Configuration > HTML Head gets neither.
  • Magento's Move JS code to the bottom of the page has Global scope and appears in the admin in developer mode only [4]. Read it on production with bin/magento config:show [5].
  • Since v1.14.9, November 14, 2024, the extension's scripts stay in the head when Move JS to Bottom is on [1].
  • Script Attributes takes one attribute per entry, such as nodefer or data-javascript-move="false", for optimizers that push scripts into the footer [1].
  • Load dataLayer before GTM Container is a troubleshooting switch for tags on a Page View trigger [1]. Leave it alone otherwise.
  • Since Magento 2.4.7, payment pages run CSP in restrict mode by default, where only whitelisted inline scripts are allowed [6], so checkout gets its own test.

Where the two snippets go, and why only there

Google's placement rule for a web container is two sentences. The first block goes "as high in the

tag as possible", the second "immediately after the opening tag" [2]. The second block is the noscript iframe for browsers without JavaScript.

In Magento, both go into the GA4 extension. The guide's first step comes before any pasting: set Google Analytics and Google Adwords to No under Stores > Configuration > Sales > Google API [1]. Then:

  1. Open WeltPixel > Google Analytics 4 Ecommerce > General Configuration [1].
  2. Paste the head block into Google Tag Manager Javascript Code. The guide: "This code is automatically placed in the head section of your Magento store" [1].
  3. Paste the body block into Google Tag Manager Non-Js Code, which the extension places in the body section [1].
  4. Save and flush the cache.

The reason for "only there" is what happens to that first snippet after you paste it. The extension renders it, so it inherits the extension's handling: the consent handling set below, the exemption from Move JS to Bottom, and the CSP nonce Magento checks on stricter pages. A second copy pasted into Content > Design > Configuration > HTML Head, or hard-coded into a theme template, gets none of that.

The consent part is a setting. With the Consent Management Method (a PRO setting) on Magento Cookie Restriction Mode, the default since the option arrived in v1.16.1 (March 6, 2026), the container does not load until the visitor accepts cookies (when Magento's own Cookie Restriction Mode, under Default Cookie Settings, is switched on; with it off, the container always loads), a choice weighed in our article on the PII controls that actually exist; server-side events wait for consent only since v1.17.4, with Require Visitor Consent for Server-Side Events switched on (it is off by default) [1]; the Conversions API addons follow the same setting, and our guide to server-side events and consent covers the per-addon details.

The fields make the container load. They do not make it do anything: generating the tags through the extension's GTM API configuration, importing them and publishing is in the complete setup guide, and the guide's warning is one line, that an unpublished container means no tracking will be available [1]. How the free GTM module and the two paid editions divide the work is in client-side GTM vs server-side tracking. If you send events to a second GA4 property, its page views need a second container in these same two fields [1]; mapping store views to GA4 properties covers when that applies.

Why can't you find Move JS to Bottom in the admin?

Because on a production store it is hidden. Adobe lists Move JS code to the bottom of the page under Stores > Settings > Configuration > Advanced > Developer > JavaScript Settings, scope Global, and adds: "These configuration settings are available in developer mode only" [4]. A merchant looking for it in production mode will not find it, and it may still be on.

Read the saved value from the command line instead. Adobe documents config:show as the command that "shows saved configuration values" [5], and the path comes from Magento's own source [7]:

bin/magento config:show dev/js/move_script_to_bottom

A 1 means the switch is on. With it on, Magento moves every <script> on the page to just before </body>, except Magento's own template scripts [8]. A GTM snippet that was placed "as high in the

tag as possible" ends up at the bottom of the page, unless something exempts it.

The extension's history with this switch is dated in its change log:

Release Date What changed
v1.10.11 July 7, 2021 Fixed Magento 2.4.2's Move JS to Bottom changing the execution order of events on the Success Page, which sometimes caused missing transactions [1]
v1.11.9 October 7, 2022 Fixed dataLayer variables marked "Other" when Move JS to Bottom ran together with Full Page Cache [1]
v1.14.9 November 14, 2024 Changed the extension's script tag so its scripts stay in the head section when Move JS to Bottom is used [1]

From v1.14.9 on, Magento's switch is covered and you can leave it however your frontend team set it. On an older build, upgrading is the fix. One flat detail from Adobe's page: the merge, bundle and minify switches next to it are Store View scope, while the move switch is Global [4], so no single store view can opt out of it.

When a third-party optimizer moves scripts anyway

Magento's switch is one mover. Optimization modules and themes bring their own, and the extension cannot know each one's rules. That is what Script Attributes is for. In the guide's words, it adds "custom attributes to the extension's script tags" so they "stay in the section of the store in cases in which you have 3rd party JS optimization functionalities that push scripts into the page footer" [1].

Three rules from the same entry:

  • Each attribute is a separate entry [1].
  • Data-attributes are allowed [1].
  • The guide's examples are nodefer and data-javascript-move="false" [1].

The attribute that works is the one your optimizer honors, so check which exemption attribute its documentation names and enter that. Treat the guide's two as examples.

Version matters here too. v1.14.11, January 15, 2025, fixed the extension's tracking scripts being moved to the bottom of the page when custom attributes were inserted into the script tag, with the change log's own rule attached: "These scripts should always be exempt from being moved, even when using functionality to defer JS" [1]. On a build older than that, the attributes you set may not have held.

The long-standing advice for this situation was to disable the optimizer. This setting exists so that disabling is not the only option.

Should the dataLayer load before the container?

Usually you never touch this. Load dataLayer before GTM Container "is a troubleshooting option. If you're experiencing inconsistent data flow in Google Analytics or Google Ads for tags that are set to fire on a Page View trigger, you can set this option to Yes as a potential solution" [1].

The history explains why it is a switch rather than a fixed order. v1.14.5, July 22, 2024, initialized the GTM script before the dataLayer. v1.14.9 then added a setting to swap the two, because "depending on store setup, speed, and other factors, you may experience more accurate client-side tracking one way or the other" [1]. The order went both ways because stores do.

The criterion is narrow, and it is the guide's: tags on a Page View trigger showing inconsistent data. A problem with tags on other triggers is outside what this setting is documented for.

In the same General Configuration group, Preconnect to Google Tag Manager (added in v1.14.17, March 12, 2025) loads the GTM script with a preconnect hint, which the change log describes as "potentially increasing performance on some installations" [1]. Whether it helps on your store is something neither the change log nor this article can tell you. Measure your own pages.

Hyvä, checkout and Content Security Policy

On Hyvä, the extension needs its compatibility modules, available for GA4 PRO [1]:

composer require weltpixel/module-hyva-ga4
composer require weltpixel/module-hyva-ga4-csp

The second is for Hyvä's CSP theme, added in v1.15.3, June 20, 2025 [1]. "The addon has no configuration options" [1]; once installed and enabled, it applies. Hyvä Checkout has been fully compatible since v1.14.9 [1]. Access to the modules is covered in our note on GA4 compatibility with the Hyvä theme.

Checkout is the page type Content Security Policy affects most. Adobe: "In Adobe Commerce and Magento Open Source version 2.4.7 and later, CSP is configured in restrict-mode, by default, for payment pages in the storefront and admin areas, and in report-only mode for all other pages", and in restrict mode "only whitelisted inline scripts are allowed" [6]. The extension's scripts carry a nonce for this: v1.14.3 improved script nonce generation "to ensure the extension's scripts properly pass Magento CSP restrictions", and v1.14.7 added a nonce to a checkout script [1]. v1.15.3 also fixed the admin control for changing Magento's CSP restriction mode, broken since 2.4.7 [1]. The full release-by-release account is in our guide to upgrading Magento 2.4.x without breaking your analytics.

If ad blockers, not Magento, are what stop your container, First-Party Tracking (PRO, labeled advanced and experimental in the guide) loads it through your own domain via a Cloudflare Worker [1].

Does the container load on every page type?

Check it the way Google documents: in your GTM workspace, click Preview to open Tag Assistant, enter your store's URL and connect [3]. The guide describes the same route [1]. If the container waits for cookie consent (see above), accept the banner in the connected tab before you judge the result.

Then walk one page of each type, because a container that loads on one template can be missing from another:

Page Why it gets its own check
Home or CMS page The baseline: container loaded, Page View tags fired
Category page A cacheable page: Move JS plus Full Page Cache is the combination behind the v1.11.9 fix [1]
Product page and cart Separate templates, so a theme override on one does not show up on the others
Checkout payment step CSP restrict mode on Magento 2.4.7 and later [6]
Success page Where Move JS to Bottom once changed the event order (v1.10.11), and where the purchase fires [1]

The DebugView and log checks that come after loading are in our guide to verifying GA4 events on Magento 2. If the container loads on the success page and purchases are still missing, the problem is past the load step, and our missing transactions troubleshooting article picks up there.

FAQ

Where do I get the two GTM snippets?

In Google Tag Manager's Workspace tab, click your container ID to open the Install Google Tag Manager dialog [2]. The first block is the script for the head, the second the noscript block for the body [2].

Can I paste the GTM code into Magento's HTML Head setting instead?

Use the extension's two fields instead. A copy in HTML Head sits outside the extension, so it gets none of the handling described above, and the fields already place the code where Google asks [1][2].

Do I need developer mode to change Move JS to Bottom?

In the admin, yes: Adobe states the JavaScript settings are available in developer mode only [4]. Adobe also documents bin/magento config:set for changing saved values from the command line [5].

Which Magento versions does the extension support?

Magento 2.3.0 through 2.4.9 and their security patches [1].

The Magento 2 Google Analytics 4 extension places your GTM container in the head and body sections of your store and keeps its scripts in the head under Magento's Move JS to Bottom, currently v1.17.5, released September 16, 2026 [1].

Sources

  1. WeltPixel Google Analytics 4 User Guide and Change Log, v1.17.5, September 16, 2026, retrieved September 28, 2026, https://docs.weltpixel.com/GA4/User-Guide-WeltPixel-Google-Analytics-4.html
  2. Install a web container, Google Tag Manager Help, retrieved September 28, 2026, https://support.google.com/tagmanager/answer/14847097
  3. Verify and publish tags, Google Tag Manager Help, retrieved September 28, 2026, https://support.google.com/tagmanager/answer/14842769
  4. Advanced > Developer, Adobe Commerce Admin configuration reference, page last updated August 11, 2026, retrieved September 28, 2026, https://experienceleague.adobe.com/en/docs/commerce-admin/config/advanced/developer
  5. Set configuration values, Adobe Commerce configuration guide, page last updated April 24, 2026, retrieved September 28, 2026, https://experienceleague.adobe.com/en/docs/commerce-operations/configuration-guide/cli/configuration-management/set-configuration-values
  6. Troubleshoot storefront checkout page in CSP restricted mode, Adobe Commerce Knowledge Base, page last updated May 22, 2026, retrieved September 28, 2026, https://experienceleague.adobe.com/en/docs/commerce-knowledge-base/kb/troubleshooting/payments/storefront-checkout-page-in-csp-restricted-mode
  7. Magento Theme module admin configuration (system.xml), magento/magento2 source at tag 2.4.9, retrieved September 28, 2026, https://github.com/magento/magento2/blob/2.4.9/app/code/Magento/Theme/etc/adminhtml/system.xml
  8. Magento Theme module JavaScript footer plugin (JsFooterPlugin.php), magento/magento2 source at tag 2.4.9, retrieved September 28, 2026, https://github.com/magento/magento2/blob/2.4.9/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php

Ready to upgrade your tracking?

Server-side tracking for Magento and Shopify — accurate data, better attribution, full privacy compliance.