What a GTM audit should actually cover
Most containers are messy but functional. The question isn't "how many problems are there" — it's whether your tracking works, your consent is compliant, and you know what to fix first. Here's what we check and why.
Ten areas of container health
A GTM audit that only checks one thing misses the picture. Consent, analytics, advertising, performance, and organization are all connected. Here's how we break it down.
Every check, explained
Search or filter to find specific checks. Each one maps to a real issue we've seen in production containers, with a description and a fix.
Dead __utmz cookie reference
Tags reference __utmz cookies from Universal Analytics, which was sunset in July 2023. These cookies are no longer set by any active system.
Dead _gat._getTrackers() call
Tags use the _gat._getTrackers() method from the legacy ga.js library, deprecated since 2014.
Dead _gaq.push() call
Tags push events to _gaq, the legacy ga.js async queue. ga.js has been deprecated since 2014.
Dead ga('send') Universal Analytics call
Tags use ga('send', ...) from analytics.js (Universal Analytics). UA was sunset July 2023.
Paused tags older than 6 months
Tags have been paused for over 6 months. Paused tags still add to container size and create maintenance confusion.
Potentially stale domain references
Tags reference domains that may no longer be active, such as previous brand names or old store URLs.
Hardcoded conversion values
Conversion tags use hardcoded dollar amounts instead of dynamic values from the data layer, leading to inaccurate revenue reporting.
Multiple googtag configs with same ID prefix
Multiple Google Tag configurations for the same platform can cause double-counting of pageviews and events.
Duplicate external script loaded
The same external script is loaded by multiple tags, creating redundant network requests and potential race conditions.
Custom HTML replaceable by native GTM template
Custom HTML implementations exist for platforms that have native GTM tag templates available.
Duplicate event listeners across tags
Multiple custom HTML tags add listeners for the same DOM events, which can cause race conditions and double-firing.
Custom event trigger with no filter conditions
Custom event triggers fire for every instance of the event regardless of context, catching unintended events.
Lookup table missing default value
Lookup table variable has no default value configured, returning undefined for unmatched inputs.
Tag setup dependency chain
Tags depend on setup tags to fire first, creating fragile firing sequences that break if timing changes.
Overloaded trigger firing 10+ tags
A single trigger fires 10 or more tags simultaneously, creating a burst of network requests that can impact page performance.
Tags with 10+ blocking triggers
Tags have 10 or more blocking (exception) triggers, creating fragile configurations that are hard to debug.
Trigger with overly broad regex pattern
Regex filters use patterns like ".*" or ".+" that match everything, providing no actual filtering.
Tag fires on both page-load and custom event
Tag fires once on page load and again when a custom event occurs, potentially double-counting conversions.
dataLayer.push race condition
Multiple Custom HTML tags push to dataLayer on the same trigger without priority ordering, creating timing-dependent behavior.
Variable referencing 5+ other variables
A variable references 5 or more other variables, creating a single point of failure in the variable chain.
Direct document.cookie access
Custom JavaScript variable accesses document.cookie directly instead of using GTM's built-in Cookie variable type.
Base64 encoding used for PII
Tags use btoa() to encode data. Base64 is trivially reversible and is not encryption — PII sent this way is still exposed.
jQuery dependency in custom HTML
Custom HTML tags use jQuery ($() or jQuery()) without guaranteed loading, causing errors if jQuery isn't present.
document.write() usage
Tags use document.write(), which blocks page rendering and can cause blank pages when fired asynchronously.
innerHTML assignment detected
Tags assign to innerHTML, which can execute embedded scripts and poses XSS risk if content includes user data.
All Pages trigger with URL-specific code
Tags fire on every page but contain URL-checking logic, wasting execution time on irrelevant pages.
eval() usage detected
Tags use eval(), which executes arbitrary strings as code. This is a critical XSS vector.
HTTP (non-secure) resource loading
Tags reference HTTP URLs instead of HTTPS, creating mixed content warnings and enabling man-in-the-middle attacks.
Empty folders found
Folders contain no tags, triggers, or variables — artifacts from previous configurations that add clutter.
Orphaned triggers (not used by any tag)
Triggers not referenced by any tag, likely leftover from deleted tags.
Orphaned variables (not referenced anywhere)
Variables not referenced by any tag, trigger, or other variable in the container.
Tags with no folder assignment
Tags are not assigned to any folder, making the container difficult to navigate at scale.
Default or placeholder names
Items still have default names like "Untitled Tag", "Copy of…", or auto-generated names.
Tags without structured naming convention
Tags lack structured naming separators (-, –, |, :), making it hard to identify purpose at a glance.
Mixed naming separators
Container uses multiple different separator styles inconsistently (dashes, pipes, colons).
Variables without type prefixes
Variables lack standard type prefixes (DLV, CJS, LUT, CONST, etc.) making it hard to identify variable types.
Duplicate custom event triggers
Multiple triggers listen for the same custom event, creating redundancy and confusion about which trigger to use.
Excessive global variable declarations
Tags declare multiple global variables (window.x) which can conflict with site code and other tags.
Event names with uppercase letters
GA4 event names use uppercase letters. GA4 treats "Add_To_Cart" and "add_to_cart" as separate events, fragmenting data.
Event names exceed 40-character limit
Event names longer than 40 characters are silently truncated by GA4, potentially merging distinct events.
Event names with invalid characters
Event names contain spaces, hyphens, or start with a number. GA4 will reject or silently rename these events.
Reserved event names used
Event names conflict with GA4 reserved names for automatic tracking (e.g., first_visit, session_start).
Events exceed 25-parameter limit
Event tags send more than 25 custom parameters. GA4 drops parameters beyond this limit silently.
Parameter names exceed 40 characters
GA4 parameter names longer than 40 characters are silently truncated.
debug_mode hardcoded to true
debug_mode is set to literal "true" instead of a GTM variable, so it stays on in production.
Duplicate event names across tags
Multiple active tags send the same event name, causing double-counting of events in reports.
GTM tags duplicating Enhanced Measurement
GTM tags send events that GA4 Enhanced Measurement also tracks automatically (scroll, click, video, etc.).
Purchase event missing required parameters
Purchase event missing transaction_id, value, or currency — required for revenue reporting in GA4.
Ecommerce events in too many separate tags
Many separate GA4 tags handle individual ecommerce events instead of consolidating with the ecommerce data layer.
Purchase event on page-load trigger
Purchase event triggered by page-load instead of a custom event trigger, which may fire before ecommerce data is ready.
Measurement ID hardcoded
GA4 measurement ID hardcoded as a literal value instead of using a GTM variable for reuse and easy updates.
Similar event name proliferation
Similar events that differ only by suffix could be consolidated (e.g., button_click_header, button_click_footer).
Parameters with PII-suggesting names
Parameters with names like "email", "phone", or "user_name" — GA4 prohibits sending PII.
Custom HTML using gtag() instead of native
Custom HTML contains gtag() calls instead of using native GA4 event tags, losing consent integration and debugging.
Event parameters with PII-risk names
Parameters with names that strongly suggest personally identifiable information is being collected.
Parameters using reserved prefixes
Parameters use reserved prefixes (ga_, google_, firebase_, gtag., or leading underscore) that GA4 silently drops.
Parameter names with inconsistent casing
The same parameter name appears with different casing across GA4 tags, fragmenting data in reports.
Parameters with inconsistent hardcoded vs dynamic values
The same parameter is used with both hardcoded strings and GTM variables across different tags.
Event tags with Once Per Page firing
GA4 event tags set to fire Once Per Page, which silently drops repeat events on single-page applications.
No Conversion Linker tag found
Google Ads tags are present but no Conversion Linker tag exists for proper cross-domain attribution.
Conversion tags on page-load trigger
Ad conversion tags triggered by page-load instead of a custom event, which may misattribute conversions.
Conversion Linker not on All Pages
Conversion Linker is not configured to fire on All Pages, breaking cross-domain tracking on some pages.
Ad tag proliferation
An ad platform has excessive tag counts (10+ native or 5+ Custom HTML), indicating consolidation opportunity.
Ad platform IDs hardcoded in 3+ tags
Ad account IDs are hardcoded in 3 or more tags instead of using a reusable GTM variable.
Custom HTML ad pixels without consent
Custom HTML ad pixels are configured with consent NOT_NEEDED or NOT_SET. Custom HTML tags have no Built-In consent protection — they fire unconditionally unless you set consent requirements in GTM.
Same platform as native tag and Custom HTML
An ad platform is deployed via both native GTM tags and Custom HTML, creating duplicate tracking.
Google Ads conversion without remarketing tag
Google Ads Conversion Tracking tags present but no remarketing tag for audience building.
Ecommerce events without Send Ecommerce data
GA4 ecommerce event tags don't have "Send Ecommerce data" enabled, so transaction data is silently lost.
Ecommerce events on page-load triggers
Ecommerce action events triggered by page-load instead of custom event, firing before data layer is ready.
Incomplete ecommerce funnel
Container tracks purchase but is missing key funnel steps (view_item, add_to_cart, begin_checkout).
Ecommerce tools via Custom HTML
Custom HTML implements ecommerce marketing platforms (Hotjar, Klaviyo, etc.) instead of native integrations.
Ecommerce tools without consent
Custom HTML ecommerce tools are configured without any consent requirement.
Ad tags firing without consent
Advertising and conversion tags have no Additional consent configured. For native Google tags (Ads Conversion, Remarketing, GA4), Built-In consent automatically enforces the correct consent signals — NOT_NEEDED is Google's recommended setup. For Custom HTML and non-Google templates, this is a real gap.
No CMP detected
Tags require consent but no Consent Management Platform is detected in the container.
CMP detected but consent mode not wired
A CMP is installed but 80%+ of tags have no consent configuration, making the CMP effectively decorative.
CMP not on Consent Initialization trigger
CMP tag is not configured to fire on the Consent Initialization trigger, causing a timing gap.
Ad tags using analytics_storage instead of ad_storage
Advertising tags have Additional consent set to analytics_storage instead of ad_storage. For native Google tags, Built-In consent already includes ad_storage — the Additional analytics_storage creates an unnecessary second gate. For non-native tags, this is the wrong consent type.
Ad tags missing ad_user_data consent
Ad tags have ad_storage but are missing the ad_user_data consent type required by the EU Digital Markets Act. Tags with Built-In ad_user_data coverage (Google Ads Conversion, Remarketing) are excluded from this check.
Remarketing tag missing ad_personalization
Remarketing tag is missing the ad_personalization consent type. Google Ads Remarketing tags have Built-In ad_personalization coverage and are excluded from this check.
GA4 events with consent NOT_NEEDED
GA4 event tags have Additional consent set to NOT_NEEDED. Native GA4 tags have Built-In analytics_storage that automatically respects Consent Mode v2 — this is Google's recommended setup. Non-native GA4 implementations need explicit consent.
GA4 tags missing analytics_storage consent
GA4 tags have Additional consent configured without analytics_storage. For native GA4 tags, Built-In analytics_storage is active regardless — the Additional types create an extra gate.
Conversion Linker missing ad_storage
Conversion Linker has consent configured without ad_storage. Native Conversion Linker tags have Built-In ad_storage coverage and are excluded from this check.
CMP waitForUpdate is 0 or missing
CMP has waitForUpdate set to 0 or not configured, so tags may fire before the user responds to the consent banner.
Non-standard consent types used
Tags use consent types not in the Consent Mode v2 specification (ad_storage, analytics_storage, etc.).
Ad tags missing ad_personalization
Ad tags require ad_storage but are missing ad_personalization. Tags with Built-In ad_personalization coverage (Google Ads Conversion, Remarketing) are excluded from this check.
How findings are prioritized
Not everything needs fixing today. Findings are grouped by what matters to the business, so you focus on real problems before cleanup tasks.
Patterns involving data loss, PII exposure, security concerns, GA4 data rejection, and consent gaps. Worth understanding before making other changes.
Things that work but could work better: data quality patterns, consent configuration, redundant tags, naming inconsistency, and performance considerations.
Naming patterns, folder organization, cleanup opportunities, and maintenance suggestions. Context for understanding the container, not urgent work.
See what's in your container
Upload your GTM export and get results in seconds. No account needed.
Scan Now