Installing Google Tag Manager on WordPress
Google Tag Manager (GTM) provides centralized control over all tracking tags without editing WordPress theme files. This guide covers WordPress-specific GTM implementation methods.
Why Use GTM with WordPress?
Benefits for WordPress Sites
- No theme edits needed to add/modify tags
- Marketing autonomy - update tracking without developer help
- Version control - track changes and rollback if needed
- Conflict prevention - avoid plugin conflicts from multiple analytics tools
- Performance - lazy load tags, fire only when needed
- Testing - preview mode validates tags before publishing
Use Cases
- Multiple tracking tools (GA4, Meta Pixel, LinkedIn Insight, etc.)
- Marketing teams need to deploy tags frequently
- A/B testing platforms (Google Optimize, VWO, Optimizely)
- Event tracking for forms, buttons, downloads
- E-commerce with complex WooCommerce tracking
Prerequisites
Create GTM Account
- Go to tagmanager.google.com
- Click Create Account
- Enter account details
- Create container (select Web)
- Copy your Container ID (format:
GTM-XXXXXX)
Copy Installation Code
- GTM provides two code snippets:
- Head snippet - goes in
<head> - Body snippet - goes after opening
<body>tag
- Head snippet - goes in
- GTM provides two code snippets:
Installation Methods
Method 1: GTM Plugin (Easiest)
Best for: Non-technical users, quick setup, minimal configuration
Recommended Plugins
1. GTM4WP (Google Tag Manager for WordPress)
Most comprehensive GTM plugin with 200,000+ active installations.
Installation:
- Navigate to Plugins → Add New
- Search for "GTM4WP" or "DuracellTomi's Google Tag Manager"
- Click Install Now → Activate
Configuration:
Settings → Google Tag Manager
Basic Settings:
Google Tag Manager ID: GTM-XXXXXX
✓ Include GTM noscript in body
✓ Placement of the GTM code: Default (in header)
□ Track logged in admins (disable for clean data)
Events:
✓ Track outbound links
✓ Track downloads
✓ Track form submissions
✓ Track internal search
Advanced:
✓ Fire tags on DOM Ready (for Elementor/Divi compatibility)
WooCommerce Integration:
Integration → WooCommerce
✓ Enable tracking of ecommerce data
✓ Track classic ecommerce
✓ Track enhanced ecommerce
Product Data:
✓ Include tax in prices
✓ Include shipping in prices
✓ Use SKU instead of product ID
Events to track:
✓ Product impressions on listings
✓ Product detail views
✓ Add to cart
✓ Remove from cart
✓ Checkout steps
✓ Purchases
Verify Installation:
// Check if GTM4WP is active and configured
if (function_exists('gtm4wp_get_the_gtm_tag')) {
echo 'GTM4WP is active';
}
2. Simple GTM
Lightweight alternative without e-commerce features.
Installation:
1. Install "Simple GTM" plugin
2. Go to Settings → Simple GTM
3. Enter Container ID: GTM-XXXXXX
4. Choose placement: Header (recommended)
5. Save changes
3. Google Site Kit
If already using Site Kit for GA4, you can add GTM:
Site Kit → Settings → Connect More Services → Tag Manager
Enter Container ID → Complete Setup
Plugin Comparison
| Feature | GTM4WP | Simple GTM | Site Kit |
|---|---|---|---|
| Setup Difficulty | Easy | Easiest | Easy |
| Data Layer Support | Full | Basic | Basic |
| WooCommerce Integration | Automatic | Manual | Manual |
| Event Tracking | Automatic | Manual | Manual |
| Page Weight | ~100 KB | ~20 KB | ~150 KB |
| Free | Yes | Yes | Yes |
Method 2: Manual Theme Integration (Most Control)
Best for: Developers, custom themes, performance optimization
Step 1: Create Child Theme
Always use a child theme to prevent losing changes during theme updates.
/wp-content/themes/your-child-theme/
├── functions.php
├── style.css
style.css:
/*
Theme Name: Your Child Theme
Template: parent-theme-folder
Version: 1.0.0
*/
functions.php:
<?php
// Enqueue parent theme styles
add_action('wp_enqueue_scripts', 'child_theme_enqueue_styles');
function child_theme_enqueue_styles() {
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
}
Step 2: Add GTM Head Code
// Add to functions.php
add_action('wp_head', 'add_gtm_head', 1);
function add_gtm_head() {
// Don't load for admin users (optional)
if (current_user_can('manage_options')) {
return;
}
$gtm_id = 'GTM-XXXXXX'; // Replace with your Container ID
?>
<!-- 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','<?php echo $gtm_id; ?>');</script>
<!-- End Google Tag Manager -->
<?php
}
Step 3: Add GTM Body Code
The body snippet is trickier because wp_body_open hook support varies by theme.
Option A: Using wp_body_open hook (WordPress 5.2+):
add_action('wp_body_open', 'add_gtm_body', 1);
function add_gtm_body() {
if (current_user_can('manage_options')) {
return;
}
$gtm_id = 'GTM-XXXXXX';
?>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<?php echo $gtm_id; ?>"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<?php
}
Option B: Direct header.php edit (if wp_body_open not supported):
Create header.php in your child theme:
<?php
// Copy content from parent theme's header.php
// Then add immediately after <body> tag:
?>
<body <?php body_class(); ?>>
<?php
// Add GTM noscript here
$gtm_id = 'GTM-XXXXXX';
?>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<?php echo $gtm_id; ?>"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<?php
// Rest of header.php content...
?>
Method 3: Using Code Snippets Plugin
Best for: Users comfortable with code but want plugin management
Install Code Snippets Plugin
- Plugins → Add New
- Search "Code Snippets"
- Install and activate
Add GTM Head Snippet
Snippets → Add New Name: GTM Head Code Code:add_action('wp_head', 'custom_gtm_head', 1); function custom_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-XXXXXX');</script> <!-- End Google Tag Manager --> <?php }Add GTM Body Snippet
Snippets → Add New Name: GTM Body Code Code:add_action('wp_body_open', 'custom_gtm_body', 1); function custom_gtm_body() { ?> <!-- Google Tag Manager (noscript) --> <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> <!-- End Google Tag Manager (noscript) --> <?php }
WordPress-Specific Considerations
Caching Plugins
GTM generally works with caching, but verify:
WP Rocket:
// Usually no exclusions needed, but if issues occur:
add_filter('rocket_exclude_js', 'exclude_gtm_from_optimization');
function exclude_gtm_from_optimization($excluded) {
$excluded[] = 'googletagmanager.com/gtm.js';
return $excluded;
}
W3 Total Cache:
Performance → Minify → Never minify the following JS files:
googletagmanager.com/gtm.js
LiteSpeed Cache:
LiteSpeed Cache → Page Optimization → JS Excludes:
googletagmanager.com
Multisite Networks
For WordPress Multisite with different containers per site:
add_action('wp_head', 'multisite_gtm_head', 1);
function multisite_gtm_head() {
$blog_id = get_current_blog_id();
// Map blog IDs to GTM container IDs
$gtm_ids = array(
1 => 'GTM-MAIN', // Main site
2 => 'GTM-BLOG2', // Blog 2
3 => 'GTM-BLOG3' // Blog 3
);
$gtm_id = isset($gtm_ids[$blog_id]) ? $gtm_ids[$blog_id] : 'GTM-DEFAULT';
?>
<!-- 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','<?php echo $gtm_id; ?>');</script>
<!-- End Google Tag Manager -->
<?php
}
Page Builders (Elementor, Divi, Beaver Builder)
GTM works with all major page builders:
- Elementor: Ensure "DOM Ready" triggers in GTM
- Divi: Use "Window Loaded" triggers for module interactions
- Beaver Builder: Standard triggers work fine
Page Builder Compatibility Check:
// Adjust GTM firing for page builders
add_filter('wp_footer', 'pagebuilder_gtm_compatibility', 99);
function pagebuilder_gtm_compatibility() {
if (did_action('elementor/loaded')) {
?>
<script>
// Ensure dataLayer is ready for Elementor
window.dataLayer = window.dataLayer || [];
</script>
<?php
}
}
AJAX and Single Page Applications (SPAs)
For WordPress sites using AJAX navigation (like Barba.js):
// Re-fire GTM on AJAX page transitions
document.addEventListener('ajaxPageLoad', function() {
dataLayer.push({
'event': 'virtualPageview',
'pageUrl': window.location.pathname,
'pageTitle': document.title
});
});
Advanced Configuration
Environment-Based Containers
Use different GTM containers for staging vs. production:
add_action('wp_head', 'environment_based_gtm', 1);
function environment_based_gtm() {
// Detect environment
$environment = defined('WP_ENVIRONMENT_TYPE') ? WP_ENVIRONMENT_TYPE : 'production';
// Set container ID based on environment
$gtm_containers = array(
'production' => 'GTM-PROD123',
'staging' => 'GTM-STAGE456',
'development' => 'GTM-DEV789'
);
$gtm_id = isset($gtm_containers[$environment]) ? $gtm_containers[$environment] : $gtm_containers['production'];
?>
<!-- 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','<?php echo $gtm_id; ?>');</script>
<!-- End Google Tag Manager -->
<?php
}
Cookie Consent Integration
Delay GTM loading until user consents:
add_action('wp_head', 'gtm_with_consent', 1);
function gtm_with_consent() {
?>
<script>
// Initialize dataLayer before GTM
window.dataLayer = window.dataLayer || [];
// Wait for consent before loading GTM
document.addEventListener('cookieConsentAccepted', function() {
(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-XXXXXX');
});
</script>
<?php
}
Performance Optimization
Preconnect to GTM domain for faster loading:
add_action('wp_head', 'gtm_preconnect', 1);
function gtm_preconnect() {
echo '<link rel="preconnect" href="https://www.googletagmanager.com">';
echo '<link rel="dns-prefetch" href="//www.googletagmanager.com">';
}
Validation and Testing
1. Install GTM Preview Extension
- Install Tag Assistant
- Navigate to tagmanager.google.com
- Click Preview in your container
- Enter your WordPress site URL
- Verify container loads
2. Check Installation in Browser
// Open browser console and check:
console.log(window.google_tag_manager);
console.log(window.dataLayer);
// Should see GTM container object and dataLayer array
3. Verify Data Layer
// Check if dataLayer exists and has data
console.log(window.dataLayer);
// Example output:
// [
// {gtm.start: 1234567890, event: "gtm.js"},
// {event: "gtm.dom"},
// {event: "gtm.load"}
// ]
4. GTM Debug Mode
In GTM interface:
- Click Preview
- Enter your WordPress URL
- Browse your site
- Check Tag Assistant panel for:
- Tags Fired
- Tags Not Fired
- Data Layer
- Variables
Common Installation Issues
GTM not loading:
- Check browser console for errors
- Verify Container ID is correct (format: GTM-XXXXXX)
- Check if ad blockers are interfering
- Ensure caching plugins aren't preventing script load
Data layer not populating:
- Verify dataLayer is initialized before GTM
- Check for JavaScript errors preventing execution
- See GTM Data Layer guide
Tags not firing:
- Check triggers in GTM Preview mode
- Verify DOM element selectors are correct
- Ensure page builder content is loaded when tag fires
See Tracking Troubleshooting for detailed debugging.
Migrating from Direct GA4 to GTM
If you already have GA4 installed directly and want to migrate to GTM:
Create GA4 Configuration Tag in GTM
- New Tag → Google Analytics: GA4 Configuration
- Enter Measurement ID
- Trigger: All Pages
Remove Direct GA4 Code from WordPress
- Disable GA4 plugin, or
- Remove manual GA4 code from theme
Test in Preview Mode
- Verify GA4 tag fires on all pages
- Check Real-Time reports in GA4
Publish GTM Container
Monitor for 48 Hours
- Compare traffic levels before/after migration
- Verify all custom events still fire
Next Steps
- Configure WordPress Data Layer for advanced tracking
- Add GA4 Tags in GTM
- Set Up WooCommerce Tracking
- Install Meta Pixel via GTM
Related Resources
- GTM Fundamentals - Universal GTM concepts
- WordPress Performance - Optimize GTM impact
- Tracking Troubleshooting - Debug GTM issues