How to Add Google Tag Manager to WordPress Without a Plugin

Google Tag Manager needs two code snippets on your site: one in the , one right after the opening tag. That’s it. Two snippets, two locations.

Yet somehow this has spawned an entire category of WordPress plugins — each one adding its own overhead to accomplish what is fundamentally a copy-paste task. I’ve seen client sites with three separate tracking plugins installed, each loading its own JavaScript, its own admin pages, its own database entries. For something that should be 10 lines of code.

Here’s how to do it properly, without the plugin bloat.

What is Google Tag Manager?

GTM is a container system. Instead of hardcoding every tracking script directly into your site (Google Analytics, Meta Pixel, LinkedIn Insight, conversion tracking, heatmaps), you install one GTM container and manage all your tags from the GTM web interface.

Change a tracking ID? Done in GTM, no site edits needed. Add a new pixel? GTM. Remove an old tag? GTM. No developer required for day-to-day tag management.

Google provides two snippets when you create a container:

1. The snippet — a JavaScript block that loads the GTM container asynchronously

2. The snippet — a

Both need to be on every page of your site. The snippet should go as high as possible in the section. The snippet should go immediately after the opening tag.

Why should you care about doing it without a plugin?

Every plugin is overhead. A GTM plugin loads its own PHP files, registers its admin page, adds database queries for settings, and sometimes loads its own JavaScript on top of GTM. You’re adding weight to install something lightweight.

Plugin conflicts. I’ve debugged sites where a GTM plugin was inserting the container code twice — once from its own logic and once from a cached version. Duplicate GTM fires mean duplicate events, inflated analytics, and doubled conversion counts. Your ad spend optimization goes out the window when your data is wrong.

Plugin dependency risk. What happens when the plugin author abandons the project, or an update breaks compatibility with your theme? Your tracking goes dark until you notice. With manual implementation, the code sits in your theme and doesn’t depend on a third-party developer’s release schedule.

You learn what’s actually on your site. When you manually add GTM, you know exactly what code runs and where. No black box. No surprise scripts. Full control.

The quick fix

Add this to your child theme’s functions.php:

// Add Google Tag Manager to head
add_action( 'wp_head', function() {
    ?>
    <!-- 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
}, 1 ); // Priority 1 = loads early in head

// Add Google Tag Manager noscript to body
add_action( 'wp_body_open', function() {
    ?>
    <!-- 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
});

Replace GTM-XXXXXXX with your actual GTM container ID. That’s the only edit needed.

Important: The wp_body_open hook requires WordPress 5.2+ and a theme that calls wp_body_open() after the tag. Most modern themes do. If yours doesn’t, the

The one-click solution

If you manage multiple tracking codes — GTM, analytics fallbacks, Meta Pixel as a backup, custom conversion scripts — typing code into functions.php for each one gets tedious fast.

OvKit includes a Tracking Codes feature under Features → Tracking where you can paste any code into designated areas: Head (top), Head (bottom), Body (after opening tag), and Footer (before closing tag). Paste your GTM snippets into the right fields, save, done.

No code editing, no child theme required, and the scripts persist through theme updates.

What happens after you fix this?

  • GTM loads on every page — your container is ready to fire tags across your entire site
  • No plugin overhead — zero additional PHP processing, database queries, or admin page loads
  • Full GTM control — manage all your tracking from the GTM interface without touching WordPress again
  • Clean data — no duplicate container loads, no plugin-injected extra scripts

Verify your installation: open your site, right-click → View Source, and search for your GTM container ID. You should find it exactly twice — once in the and once in the

For extra verification, install the Tag Assistant Chrome extension. It confirms your GTM container is loading correctly and shows you every tag that fires.

FAQ

Can I use this method for other tracking scripts like Meta Pixel or LinkedIn Insight?

Yes, but don’t. If you’re using GTM, add those pixels through GTM as tags — not as additional hardcoded scripts. The whole point of GTM is centralized tag management. Adding Meta Pixel both in GTM and directly in your theme means duplicate tracking, inflated numbers, and confused ad algorithms.

Does Google Tag Manager slow down my site?

GTM itself loads asynchronously, so it doesn’t block page rendering. The script is small (under 30 KB gzipped) and served from Google’s CDN. What can slow your site is the tags inside GTM — loading 15 different tracking pixels, heatmap tools, and chat widgets through GTM still adds up. GTM is the delivery mechanism; the contents matter more than the container.

Should I use wp_head or wp_enqueue_scripts for GTM?

Neither wp_enqueue_scripts hook is appropriate here — GTM isn’t a WordPress-registered script with a handle. Use wp_head with priority 1 to ensure it loads as early as possible in the . The GTM documentation specifically recommends placing the snippet as high in the as possible, and wp_head with low priority achieves that.


Related reads: