To install Google Tag Manager on WordPress, create a web container at tagmanager.google.com, then place its two snippets on every page: the JavaScript block as high in <head> as possible, and the <noscript> block immediately after the opening <body> tag. A plugin or the wp_body_open hook handles the second one.
wp_head and wp_body_open. Then add a Google Tag with your GA4 ID on an Initialization trigger, test in Preview, and hit Submit to publish a version.This assumes you have admin access to WordPress and a Google account. Tag Manager is free. Fifteen minutes of setup buys you the ability to add, edit and remove tracking code for the rest of the site’s life without ever opening a PHP file again, which is the whole point.
Tag Manager is not Google Analytics
This confusion costs people days. Tag Manager does not measure anything. It is a container: a small script that loads on your pages and then injects whatever other scripts you have configured, when you have told it to. Google Analytics 4 is one of the things it can inject. So is a Google Ads conversion tag, a LinkedIn pixel, or a chat widget.
| Google Tag Manager | Google Analytics 4 | |
|---|---|---|
| What it is | A tag management container | An analytics and reporting product |
| ID format | GTM-XXXXXXX (container ID) | G-XXXXXXXXXX (Measurement ID) |
| Gives you reports | No | Yes |
| Where you see the data | Nowhere — only a debug panel | analytics.google.com |
| Do you need the other one? | Useless on its own | Works fine without GTM |
If all you want is basic traffic reporting, you do not need Tag Manager at all — our guide on how to add Google Analytics to WordPress covers the simpler routes. Come back here when you have a second or third tag to manage.
Step 1: Create an account and a web container
- Go to tagmanager.google.com and click Create account.
- Give the account your company or site name and pick your country. One account per business; one container per website.
- Under Container setup, enter your domain as the container name and choose Web as the target platform. Ignore iOS, Android, AMP and Server unless you know you need them.
- Accept the terms. A dialog appears with your two code snippets and the container ID, which looks like
GTM-XXXXXXX. You can reopen it any time from Admin → Install Google Tag Manager.
Step 2: Where each snippet goes
The head snippet is the one that does the work. It creates the dataLayer array and loads gtm.js. Google asks for it “as high in the <head> tag as possible” so that tags fire before the rest of the page competes for bandwidth.
The body snippet is a <noscript> iframe that goes immediately after the opening <body> tag. It is a fallback for visitors with JavaScript disabled. Skipping it does not break normal tracking, which is why so many installs quietly omit it — but Tag Manager’s own diagnostics will flag it, and it takes one hook to do right.
<?php
// Child theme functions.php. Replace GTM-XXXXXXX with your container ID.
add_action( 'wp_head', 'geekblog_gtm_head', 1 );
function geekblog_gtm_head() {
?>
<!-- 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 -->
<?php
}
add_action( 'wp_body_open', 'geekblog_gtm_body' );
function geekblog_gtm_body() {
?>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<?php
}When your theme has no wp_body_open
wp_body_open() arrived in WordPress 5.2 and every theme in the directory is supposed to call it. Older and sloppier themes do not, and the hook then fires nowhere. Two fixes:
- Add the call yourself. In your child theme’s
header.php, put<?php wp_body_open(); ?>on the line directly after the opening<body>tag. Copyheader.phpinto the child theme first so an update does not undo it. - Accept the loss. Hook the noscript block to
wp_footerinstead. It is technically the wrong position but the fallback still resolves, and no JavaScript user was ever going to notice.
header.php to edit. Their body tag is generated by WordPress core, which does call wp_body_open(), so the hook works — but you cannot paste raw code into a template part and expect it to land in the right place. Use the hook or a plugin, not the Site Editor’s custom HTML block.Plugin route or manual route?
A dedicated GTM plugin such as GTM4WP handles both snippets, places the noscript block correctly on themes that support the hook, and adds WooCommerce and form integrations that push structured data into the data layer for you. On a store, that last part alone is worth it — hand-building ecommerce data layer events is genuinely tedious work.
The manual child-theme route is lighter and has no update surface. I use it on simple content sites and use the plugin anywhere with a cart. Either way the container ID and configuration end up in your database, not in a file, which matters when you are auditing a site you did not build — see where WordPress plugin settings are stored for how to find them.
Step 3: Configure GA4 inside the container
Get your Measurement ID from Admin → Data collection and modification → Data streams in Google Analytics, then in Tag Manager:
- Open Tags → New and click Tag Configuration.
- Choose Google Tag.
- Paste your
G-XXXXXXXXXXinto the Tag ID field. - Click Triggering and pick Initialization – All Pages. The initialization trigger runs before ordinary page-view triggers, which is what you want for a tag that everything else depends on.
- Name it something obvious like “Google tag — GA4” and save.
That single tag is enough. GA4’s enhanced measurement will start collecting page_view, scrolls, outbound clicks, file downloads and form events on its own. You only need extra GA4 event tags for things enhanced measurement does not cover, which is the territory our guide to tracking a custom event in Google Analytics covers in detail.
Step 4: Preview mode and the debug panel
Click Preview in the top right. Tag Manager opens Tag Assistant, asks for your site URL, and launches your site in a new tab with a debug connection attached. The debug panel shows a timeline of events down the left side and, for each one, which tags fired and which did not. Click a tag name to see the exact values it sent.
Three things to check while you are in there: your Google Tag fires on Initialization, it fires exactly once per page, and the Measurement ID it sends matches your property. Then open GA4’s Realtime report in another tab and confirm your clicks appear.
Step 5: Publish a version, and know how to undo it
Nothing you build is live until you publish. Click Submit, review the workspace changes, name the version something meaningful (“Add GA4 Google tag” beats “Version 3”), write a one-line description, and click Publish. If you want to bank the work without pushing it live, choose Create Version instead.
Rollback is the underrated feature. Open Versions, find the last version that worked, open its Actions menu and choose Publish To… to push it live immediately, or Set as Latest Version to load it back into your workspace for editing first. This is why version descriptions matter: at 2am you want to know which version was the one before the pixel broke.
What to track first
| Interaction | Already free in GA4? | GTM trigger to use | Why bother in GTM |
|---|---|---|---|
| Outbound clicks | Yes (click) | Just Links, with a hostname condition | Separate affiliate links from ordinary ones |
| Form submits | Partly (form_submit) | Form Submission, or a Custom Event from the plugin | AJAX forms never fire the native event |
| Scroll depth | Yes, at 90% only | Scroll Depth at 25/50/75/90% | Real engagement curves, not one flag |
| File downloads | Yes (file_download) | Just Links matching your file extensions | Catch extensions Google’s list misses |
Notice how much is already free. Turn enhanced measurement on before you build anything, then only build what is genuinely missing. Once the container is in place, adding a non-Google tag is the same three steps — that is how you would drop in something like the TikTok pixel without touching WordPress.
Troubleshooting
“No tags fired” in Preview
If the debug panel connects but every tag is under “Tags Not Fired”, the container is installed and the problem is your triggers. Check that the trigger type matches the event you are performing, and that any conditions (page path, click URL, form ID) are not case-sensitive mismatches. If the debug panel never connects at all, the container is not on the page: view source and search for gtm.js?id=GTM-.
The body snippet is missing on a block theme
Load a page, view source, and search for ns.html?id=. If it is absent, either your plugin is set to skip the noscript block or the hook is not running. On a block theme the fix is almost never editing a template — use a GTM plugin, or the wp_body_open hook from a small plugin file. It is a cosmetic issue for the vast majority of sites; do not tear your theme apart over it.
GA4 double-counting
The classic mistake: you add GA4 through Tag Manager while gtag.js is still hardcoded in your theme or injected by an analytics plugin. Every page view lands twice, and engagement metrics go strange. Pick one path. Tag Assistant will list both tags on the page, which makes the diagnosis quick.
GTM hurting Core Web Vitals
The container script itself is small. The damage comes from what you put in it. A few habits that keep the cost down:
- Audit the container quarterly and delete tags for tools you stopped paying for. Dead pixels are the single most common cause of a bloated container.
- Move non-critical tags off All Pages onto a Window Loaded or timer trigger so they stop competing with your largest contentful paint.
- Never inject a large third-party library through a Custom HTML tag when the vendor offers a lightweight snippet or an async loader.
- Measure the difference. Run PageSpeed Insights before and after publishing a version, and use GTM’s own version history to roll back if a tag costs you more than it earns.
Google’s stated benefit of the 2026 container unification is that data goes straight to Google destinations without loading extra JavaScript, so this should get better rather than worse. Check Google’s own install instructions when in doubt about snippet placement — it is the one page worth bookmarking.
Frequently asked questions
Do I need Google Analytics if I install Google Tag Manager?
Yes. Tag Manager collects and reports nothing on its own; it only delivers other people’s tags. You still need a GA4 property and Measurement ID to get traffic reports. Installing GTM without configuring a GA4 tag inside it gives you zero data.
Can I install Google Tag Manager without a plugin?
Yes. Hook the head snippet to wp_head with priority 1 and the noscript snippet to wp_body_open in a child theme’s functions.php. That is lighter than any plugin. Use a plugin instead if you run WooCommerce and want ecommerce data layer events built for you.
What happens if I skip the body snippet?
Normal JavaScript-enabled tracking works exactly the same, because the noscript iframe only serves visitors with JavaScript disabled. You lose a tiny fallback and Tag Manager may warn you the install is incomplete. Add it if you can; do not delay launch over it.
Why is my container installed but no tags fire?
Because a container with no configured tags does nothing, or because your triggers do not match the events you are testing. Open Preview, perform the interaction, and read the “Tags Not Fired” list — clicking a tag there shows exactly which trigger condition failed.
How do I undo a bad Tag Manager change?
Open Versions, find the last good version, and use the Actions menu. Publish To… pushes it live immediately; Set as Latest Version loads it into your workspace so you can edit before republishing. Rollback is instant and needs no developer.
Wrapping up
Install the container once, both snippets, in a child theme or a maintained plugin. Add a single Google Tag on an Initialization trigger, verify it in Preview and in GA4 Realtime, then publish a named version. Everything after that is configuration you can do from a browser on a Sunday afternoon.
The discipline that matters is not technical: name your versions, delete tags you no longer use, and check page speed after each publish. Do that and Tag Manager stays an asset. Skip it and you end up with a container nobody understands. When you are ready to turn the events into something a client will pay for, our guide to using Google Analytics for marketing is the next step. Running a different CMS? The same two-snippet logic applies — see how to create a Joomla website for where the template hooks live there.

