After auditing dozens of GTM containers, the same mistakes appear over and over. Some are innocent setup errors. Others are ticking time bombs, containers that look like they’re working until someone checks the data and finds three years of inflated sessions and double-counted conversions.
Here are the seven most damaging GTM mistakes we find in client containers, and exactly how to fix each one.
Mistake #1: Duplicate Tags Firing on Every Page
What it looks like: GA4 pageview events showing up twice in GA4 DebugView. Session counts are 1.5–2x higher than your actual traffic. Reports look inflated but you can’t figure out why.
Why it happens:
- Someone installed the GA4 tag via GTM and hardcoded the gtag.js snippet in the page
<head> - A second GTM container was added to the site without removing the first
- The GA4 Configuration Tag was duplicated inside the container (two tags with the same Measurement ID)
How to diagnose:
Open Chrome DevTools → Network tab → filter by collect? or g/collect. If you see two requests firing on every pageview, you have a duplicate.
// In the browser console, check how many GA4 instances are running:
window.dataLayer.filter(d => d[0] === 'config')
// Should show ONE entry per page load. Multiple = duplicates.
Also check the page source (Ctrl+U) and search for G-XXXXXXXXXX (your Measurement ID). It should appear only once, inside the GTM script.
How to fix:
- Search the site’s codebase for the Measurement ID, remove any hardcoded
gtag.jsorga.jssnippets - Inside GTM, check for duplicate Configuration Tags: GTM → Tags → search your Measurement ID
- Verify only one GTM container snippet is on the page
Reference: GA4 Deduplication in GTM
Mistake #2: The All Pages Trigger on Conversion Tags
What it looks like: Your “thank you page” event or purchase event fires on every page, not just the confirmation page. Your conversion numbers are wildly inflated.
Why it happens: Someone created a conversion tag and used the All Pages trigger instead of a page-specific trigger. This is one of the most common mistakes made by developers who are new to GTM.
How to diagnose:
In GTM Preview mode, navigate to your homepage. If your conversion tag (e.g., GA4 - Purchase Event) fires on the homepage, it’s broken.
How to fix:
Create a proper trigger for confirmation/thank-you pages only:
- GTM → Triggers → New
- Trigger Type: Page View
- Fire on: Some Page Views
- Condition:
Page Path→contains→/order-confirmation(or your actual path)
For e-commerce, the better approach is to use a dataLayer event trigger:
// Push this from your order confirmation page:
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: 'T_12345',
value: 59.99,
currency: 'USD',
items: [...]
}
});
Then in GTM, trigger on Custom Event → Event name: purchase.
Reference: GTM Trigger types
Mistake #3: Missing or Broken dataLayer Variables
What it looks like: Your GA4 events fire, but custom dimensions are blank. Product names, categories, prices are missing from e-commerce reports. You see undefined or (not set) in GA4.
Why it happens: GTM variables are referencing dataLayer keys that either:
- Don’t exist (typo in the key name)
- Haven’t been pushed yet when the tag fires
- Are nested incorrectly (e.g.,
ecommerce.items[0].item_namevsecommerce.items.0.item_namein GTM variable syntax)
How to diagnose:
In GTM Preview, click on a tag that’s supposed to send product data. In the Variables tab, look for variables showing as undefined.
Also check the dataLayer panel, expand the relevant event and verify the key names match exactly what your GTM variables reference.
Common variable path issues:
| What you want | GTM variable path |
|---|---|
ecommerce.value | ecommerce.value |
| First item’s name | ecommerce.items.0.item_name |
ecommerce.items[0].price | ecommerce.items.0.price |
GTM uses dot notation for nested objects. Bracket notation ([0]) doesn’t work in GTM variable paths.
How to fix:
- Open GTM Preview → click the event → Variables tab
- Find any variable showing
undefined - Cross-reference the variable’s Data Layer Variable Name with the actual dataLayer push from your developer
- Correct any typos or nesting mismatches
For debugging, add this to your browser console:
// See everything in the dataLayer
window.dataLayer.forEach((entry, index) => {
console.log(index, JSON.stringify(entry, null, 2));
});
Mistake #4: Tags Firing in Preview/Debug Mode But Not in Production
What it looks like: Everything works perfectly in GTM Preview. You publish. GA4 DebugView stops showing events. Your live reports never see the data.
Why it happens (three common causes):
A) Tag blocked by Content Security Policy (CSP) Your site’s CSP header blocks GTM’s domains from loading scripts or making requests. GTM works in Preview because Preview injects differently.
Check: Open DevTools → Console. Look for CSP errors like:
Refused to load script from 'https://www.googletagmanager.com'
Fix: Add GTM’s domains to your CSP script-src and connect-src directives:
script-src 'self' https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com https://analytics.google.com;
B) Trigger fires on URL pattern that doesn’t exist in production
Your trigger uses Page URL contains staging.yoursite.com or a development path that doesn’t exist live.
Fix: Review all trigger conditions. Remove environment-specific URL conditions.
C) Tag never published (Draft status) GTM containers have versions. If a tag is in a workspace but no new version was published, it won’t run on the live site.
Fix: GTM → Submit → Publish new version. Verify the live container version number matches.
Mistake #5: Cross-Domain Tracking Not Configured
What it looks like: Users who move from yoursite.com to shop.yoursite.com (or to a payment processor) show up as two separate sessions. Conversion attribution is broken, GA4 attributes the purchase to (direct) instead of the original traffic source.
Why it happens: GA4 and GTM need explicit configuration to pass the _ga session cookie across domains.
How to diagnose:
- In GA4 → Reports → Traffic Acquisition, check if a huge percentage of conversions come from
(direct) / (none) - This is a red flag, real users rarely type URLs directly before purchasing
How to fix in GA4:
- GA4 → Admin → Data Streams → your web stream
- Configure tag settings → Configure your domains
- Add all domains that should share session data:
yoursite.comshop.yoursite.comcheckout.paymentprovider.com(if applicable)
How to fix in GTM:
If using GTM’s GA4 Configuration Tag:
- Open your GA4 Configuration Tag
- Fields to Set → Add field:
- Field Name:
linker - Value:
{"domains": ["shop.yoursite.com", "yoursite.com"]}
- Field Name:
Reference: GA4 cross-domain measurement
Mistake #6: Excluding Internal Traffic But Not Bot Traffic
What it looks like: Your traffic numbers look healthy, but engagement rate is unusually low. Bounce rate is high from certain sources. Your own visits inflate session counts.
Why it happens: Most people know to exclude their own IP in GA4, but forget about:
- Office IPs (when the team works from an office)
- Contractor and agency IPs
- Bot and crawler traffic
- Staging/preview environments
How to fix internal traffic:
In GA4:
- Admin → Data Streams → your stream → Configure tag settings
- Define internal traffic → add your IP ranges
- Admin → Data filters → Internal Traffic → set to Active
In GTM (alternative approach):
Create a Lookup Table variable that returns true for known internal IPs, then add an exception trigger to all tags.
How to fix bot traffic:
GA4 automatically filters known bots (IAB list), but unknown bots slip through.
- GA4 → Admin → Reporting Identity
- Verify “Filter out known bots and spiders” is enabled (it should be by default)
For additional bot filtering, create a GA4 audience exclusion or use a custom filter:
// In GTM, block tags from firing on headless browsers
// (Many bots don't have userAgent strings with standard browser identifiers)
{{JS - Is Not Bot}} variable:
function() {
return !/bot|crawler|spider|crawling/i.test(navigator.userAgent)
&& navigator.webdriver !== true;
}
Reference: GA4 data filters
Mistake #7: Firing the Purchase Event Without Deduplication
What it looks like: Your GA4 purchase events and revenue figures are significantly higher than your actual orders. You have a Shopify store where purchase fires 1.8x per transaction on average.
Why it happens: The order confirmation page can be:
- Refreshed by the user (double submission)
- Hit by the browser’s back button
- Cached and re-served
Without deduplication, the purchase event fires every time the confirmation page loads, even for the same order.
How to fix:
Method 1: Transaction ID deduplication in GA4
GA4 automatically deduplicates purchase events with the same transaction_id within a 7-day window, but only if you’re consistently sending the transaction_id parameter.
Verify your purchase event includes it:
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: 'ORDER-12345', // ← This MUST be unique per order
value: 59.99,
currency: 'USD',
items: [...]
}
});
Method 2: sessionStorage flag
// On the confirmation page:
const orderId = 'ORDER-12345';
const key = `tracked_${orderId}`;
if (!sessionStorage.getItem(key)) {
sessionStorage.setItem(key, '1');
window.dataLayer.push({
event: 'purchase',
ecommerce: { transaction_id: orderId, ... }
});
}
Method 3: GTM trigger with custom condition
In GTM, create a JavaScript variable that returns true only if the order ID hasn’t been tracked this session, then add it as a trigger condition on your purchase tag.
Reference: GA4 ecommerce measurement
Run a GTM Container Audit
Before your next campaign launch, run through this checklist:
□ Search page source for duplicate GTM containers or hardcoded GA tags
□ Preview mode: check each tag fires on the RIGHT pages only
□ Preview mode: no conversion tags fire on the homepage
□ All dataLayer variables return actual values (not undefined)
□ Cross-domain tracking configured if using multiple subdomains
□ Internal traffic filter active in GA4
□ Purchase event includes transaction_id
□ All tags are published (current container version is live)
□ Consent Mode v2 defaults set before any tags fire
When to Get Help
If your GA4 data doesn’t match your Shopify, Stripe, or CRM data by more than 15%, something is broken. The earlier you find it, the less historical data is poisoned.
We do GTM container audits as a standalone engagement, you get a documented list of every broken tag, variable, and trigger, with prioritised fixes. Book a free consultation to discuss your setup.
Share this article