700,000+
October 1, 2026
September 23, 2013
Google Tag Manager (GTM) is Google’s free tool for everyone to manage and deploy analytics and marketing tags as well as other code snippets
using an intuitive web UI. To learn more about this tool, visit the official website.
This plugin places the GTM container code snippets onto your WordPress website so that you do not need to add them manually.
Multiple containers are also supported, and each container can have its own environment parameters, custom domain and custom path.
The plugin complements your GTM setup by pushing page meta data and user information into the so-called data layer.
Google’s official help pages include more details about the data layer.
PHP 8.0 and WordPress 6.3 are required to use this plugin.
The original GTM container code is divided into two parts:
The first part is a javascript code snippet that is added to the <head> section of every page of the website.
This part is critical to enable all features of GTM, and this plugin helps to place this part
correctly on your site.
The second part is an iframe snippet that acts as a failsafe/fallback should users’ JavaScript be disabled.
Google recommends – for best performance – to place this code snippet directly after the opening <body> tag on each page.
Albeit not ideal, it will work when placed lower in the code. This plugin provides a code placement option for the second code snippet.
If your WordPress theme is compatible with the additions of WordPress 5.2, then this plugin will place this second snippet in the right place.
Users of the Genesis theme, GeneratePress theme, Elementor, Oxygen Builder and Beaver Builder Theme will also have it placed correctly.
To utilize this, set the compatibility mode in the plugin options to off.
All other users can place this second code snippet using custom PHP code (the “Manually coded” option), or select the so-called “Footer” option
to add the code lower on the page (this is not the recommended way, but it will work).
Optional page variables that help with behavior tracking and Google Analytics 4 content grouping:
Data is collected in the browser using User-Agent Client Hints and pushed as a gtm4wp.deviceData event.
Note that Safari and Firefox expose less detail than Chromium based browsers.
Track users’ interactions with any embedded media:
You can choose to fire data layer events when the media player loads, when the media is played, when it is paused or stopped,
and optionally when the user reaches 10, 20, 30, …, 90, 100% of the media duration. Each event also populates Google Tag
Manager’s built-in Video variables (Video Status, Video URL, Video Title, Video Provider, Video Duration, Video Current Time,
Video Percent, Video Visible).
Tracking is supported for embedded media using the built-in oEmbed feature of WordPress, as well as most other media plugins
and copy/pasted embed codes. Players inserted into the page after it has loaded (for example in a popup, a lightbox or via
AJAX) can also be tracked by turning on the optional “Track dynamically inserted players” setting.
To increase website security, you have the option to allowlist or blocklist tags, triggers and variables.
You can prevent specific tags from firing, or prevent the use of certain variable types, regardless of your GTM setup.
If the Google account associated with your GTM account is compromised, an attacker could easily
execute malware on your website without accessing its code on your hosting server. By blocklisting custom HTML tags,
custom JavaScript variables and sandboxed scripts (custom tag and variable templates), you can secure the Tag Manager container.
Google Tag Manager for WordPress integrates with several popular plugins. More integrations to come!
If you are using a server side container,
you can enter your custom domain name and custom path to load gtm.js from there. Both can be set per container, so you can mix
server side and standard containers on the same site.
(experimental, off by default)
On sites using a full page cache (LiteSpeed, WP Rocket, Varnish, Cloudflare APO), the HTML generated for one visitor is served
to everyone else as well. Any visitor specific value written into the data layer would therefore leak to other visitors – the
classic example being a page cached while an editor was logged in, then served to anonymous visitors with that editor’s email
address and role still in the data layer.
When this option is enabled, no visitor or session data is written into cacheable HTML at all. Those values are delivered in the
browser instead, under the same data layer variable names, so your existing Google Tag Manager setup keeps working.
You can export all plugin settings into a JSON file and import them on another site, which makes it easy to roll out the same
configuration across several websites. Imported files are treated as untrusted and every value is validated before it is stored.
You can set which user roles need to be excluded from tracking when a user with that role visits the frontend. This will completely disable the container code for that user.
The container can be limited to production environments only, so a cloned or staging copy of your site does not send data into
your production Google Tag Manager container. This relies on the WP_ENVIRONMENT_TYPE setting of WordPress.
Version 2.0 is a complete object oriented rewrite. Every feature is a module, and third party plugins can register their own
modules through the gtm4wp_register_modules action. All public template functions, filter and action names, wp-config constants
and the option storage key of the 1.x versions are unchanged, so existing integrations keep working.
duracelltomi-google-tag-manager-for-wordpress to the /wp-content/plugins/ directoryTutorials for various Google Tag Manager settings and implementation are available on the plugin’s website:
https://gtm4wp.com/setup-gtm4wp-features
PayPal and some other 3rd party payment gateways do not redirect users back to your website after a successful transaction by default.
They offer a route back for your customer, but it can happen that users close the browser before arriving at your thank you page
(also known as the order received page). This means that neither Google Analytics tags nor any other tags have a chance to fire.
Enable auto-return in your payment gateway settings. This will instruct the gateway to show a quick info page after payment
and redirect the user back to your site. This will improve the accuracy and frequency of tracked transactions.
This can happen if you are using a 3rd party plugin that alters the default order received page in a way that does not use
WooCommerce’s integration hooks. Either stop using that plugin, or ask its authors to mimic the behavior of the default order
received page more closely by supporting the woocommerce is_order_received_page function and the woocommerce_thankyou action.
Since version 2.0 the plugin also offers two settings that work around this without changing the 3rd party plugin: “Custom order
received (thank-you) page” fires the purchase event on a bespoke confirmation page, and “Reliable purchase tracking” emits a
missed purchase event on the next page the customer views in the same browser session. Both are de-duplicated, so an order is
never counted twice.
Although Google recommends blocklisting tags and variables using classes, people struggle to know
which tags and variables get affected. Therefore I opted for individual tags and variables rather than classes
on the tag restriction tabs.
Regarding variables: ensure they are not part of any critical tags, as blocklisting such variables will render those tags useless.
Google Tag Manager supports basic scroll depth tracking based on percentage or pixels natively. Add a Scroll Depth
trigger in your container and use it to fire your Google Analytics 4 and/or Google Ads remarketing/conversion tags.
(The plugin’s own scroll tracking feature was removed in 2.0 in favor of this built-in GTM functionality.)
Yes, this is a real risk with any full page cache, and it is not specific to this plugin: the HTML generated for one visitor is
stored and served to everyone else. If visitor specific values (email address, user role, IP address) are written into the data
layer, the cached copy will contain the values of whoever happened to trigger the caching.
Turn on the “Cache-safe data layer” option to avoid this. With it enabled, no visitor or session data is written into cacheable
HTML; those values are delivered in the browser instead, under the same data layer variable names.
Each tracking feature (WooCommerce, each media player, Contact Form 7, device data, etc.) is its own small
JavaScript file, and the plugin loads only the files a page actually needs – the YouTube tracker, for instance,
loads only on pages that embed a YouTube video, and with the “defer” strategy so it never blocks rendering.
Those files are already minified by the plugin’s build.
Merging several files into one is intentionally left to a caching / performance plugin (WP Rocket, Autoptimize,
LiteSpeed Cache, etc.), which can combine scripts across your whole site and in a way that suits your hosting and
HTTP setup. On modern HTTP/2 hosting, many small conditionally-loaded files usually perform as well as one combined
file. (1.x combined its own scripts; 2.0 delegates this.)
gtag. Since 2.0.3, a JavaScript delay plugin with gtag on its keyword list, such as Flying Scripts, delayed the whole block and the browser console showed dataLayer is not defined. The Google tag developer ID is still set.dataLayer_content is then an empty object and is not pushed. Content that is pushed is always an object, never an array, which GTM would read as a command.Fixed: item_id, sku and the dynamic remarketing id are now always strings. A product with no SKU falls back to its numeric id, and that fallback was written into the data layer as a number while the same fields are strings on every product that has a SKU. Check your GTM setup if a trigger or variable compares one of those fields against a number.
Fixed: the begin_checkout event was missing on stores where WooCommerce reports the checkout page as the cart page as well, which happens when a plugin or a theme forces that decision or when a cart shortcode is left in the checkout page content. Such a checkout page pushed view_cart and begin_checkout never fired anywhere. The checkout is now decided first, so a page reported as both is treated as the checkout page. The order-received page is recognized ahead of both on both halves as well: on such a store the thank-you page used to be taken for the cart page by the block tracker, which loaded there in its cart context in place of the regular tracker.
Major rewrite of the plugin. Please read the announcement post on gtm4wp.com before upgrading. This section is a summary; wordpress.org truncates a changelog after 5,000 words, so the complete list of 2.0.0 changes with every detail lives in the changelog on GitHub: https://github.com/duracelltomi/gtm4wp/blob/master/CHANGELOG.md
gtm.allowlist / gtm.blocklist keys, the entity list is refreshed from Google’s documentation, and blocklist mode no longer blocks every tag.$gtp4wp_plugin_url, $gtp4wp_plugin_basename and $gtp4wp_script_path global variables, which will be removed in 2.1.A maintenance release for the 1.x line. 1.22.4 was intended to be the last one before GTM4WP 2.0; this release exists because the fixes below are worth shipping to 1.x users rather than holding for 2.0.
Older releases are listed in the full changelog on GitHub: https://github.com/duracelltomi/gtm4wp/blob/master/CHANGELOG.md
| Version | Download | Type |
|---|---|---|
| 2.0.5 | Download | Stable |
| 2.0.4 | Download | Stable |
| 2.0.3 | Download | Stable |
| 2.0.2 | Download | Stable |
| 2.0.1 | Download | Stable |
| 2.0.0 | Download | Stable |
| 1.22.5 | Download | Stable |
| 1.22.4 | Download | Stable |
| 1.22.3 | Download | Stable |
| 1.22.2 | Download | Stable |
| 1.22.1 | Download | Stable |
| 1.22 | Download | Stable |
| 1.21.1 | Download | Stable |
| 1.21 | Download | Stable |
| 1.20.3 | Download | Stable |
| 1.20.2 | Download | Stable |
| 1.20.1 | Download | Stable |
| 1.20 | Download | Stable |
| 1.19.1 | Download | Stable |
| 1.19 | Download | Stable |
| 1.18.1 | Download | Stable |
| 1.18 | Download | Stable |
| 1.16.2 | Download | Stable |
| 1.16.1 | Download | Stable |
| 1.16 | Download | Stable |
| 1.15.2 | Download | Stable |
| 1.15.1 | Download | Stable |
| 1.15 | Download | Stable |
| 1.14.2 | Download | Stable |
| 1.14.1 | Download | Stable |
| 1.14 | Download | Stable |
| 1.13.1 | Download | Stable |
| 1.13 | Download | Stable |
| 1.12.3 | Download | Stable |
| 1.12.2 | Download | Stable |
| 1.12.1 | Download | Stable |
| 1.12 | Download | Stable |
| 1.11.6 | Download | Stable |
| 1.11.5 | Download | Stable |
| 1.11.4 | Download | Stable |
| 1.11.3 | Download | Stable |
| 1.11.2 | Download | Stable |
| 1.11.1 | Download | Stable |
| 1.11 | Download | Stable |
| 1.10.1 | Download | Stable |
| 1.10 | Download | Stable |
| 1.9.2 | Download | Stable |
| 1.9.1 | Download | Stable |
| 1.9 | Download | Stable |
| 1.8.1 | Download | Stable |
| 1.8 | Download | Stable |
| 1.7.2 | Download | Stable |
| 1.7.1 | Download | Stable |
| 1.7 | Download | Stable |
| 1.6.1 | Download | Stable |
| 1.6 | Download | Stable |
| 1.5.1 | Download | Stable |
| 1.5 | Download | Stable |
| 1.4 | Download | Stable |
| 1.3.2 | Download | Stable |
| 1.3.1 | Download | Stable |
| 1.3 | Download | Stable |
| 1.2 | Download | Stable |
| 1.1.1 | Download | Stable |
| 1.1 | Download | Stable |
| 1.0 | Download | Stable |
| 0.9.1 | Download | Stable |
| 0.9 | Download | Stable |
| 0.8.2 | Download | Stable |
| 0.8.1 | Download | Stable |
| 0.8 | Download | Stable |
| 0.7.1 | Download | Stable |
| 0.7 | Download | Stable |
| 0.6 | Download | Stable |
| 0.5.1 | Download | Stable |
| 0.5 | Download | Stable |
| 0.4 | Download | Stable |
| 0.3 | Download | Stable |
| 0.2 | Download | Stable |
| 0.1 | Download | Stable |
| Development | Download | Trunk |