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.