The GTM container audit checklist agencies actually use
Most GTM containers are 30% dead tags by line count. This is the 12-point audit that finds the dead weight, the misconfigured triggers, and the silent CSP-blocked tags — without breaking anything live.
Inherit a GTM container that's been edited by 4 different contractors over 3 years and you'll find the same things every time: tags that fired once in 2022 and have been silently broken since, a "Page View - All Pages" trigger duplicated under three different names, and at least one consent-mode-blocked tag nobody noticed got blocked.
The audit is mechanical. 12 checks, ~45 minutes, no cleverness required. This is the version we run on every new client.
Before you start
Get into Preview mode on the live container, not a new draft. You want to see what's actually firing for real users today, not what would fire after the next publish. Use a clean browser profile (no extensions) plus an incognito/private window with extensions enabled, so you see both the "ad blocker present" and "ad blocker absent" pictures.
The 12 checks
1. Active vs. paused vs. deprecated tags
Go to Tags → sort by status. Three buckets:
- Active: should be firing on real traffic. Confirm at least one fired in the last 7 days via the GTM debug panel or the platform's own dashboard (GA4 Realtime, Google Ads conversion tracking status, etc.).
- Paused: harmless, but document why each is paused. If nobody knows, the answer is usually "delete it."
- Deprecated (legacy
_legacy/_old/_v1naming): these accumulate forever. Verify they don't fire on any current trigger, then archive.
2. Trigger-to-tag map
For each active tag, identify exactly which trigger fires it. Look for:
- Tags fired by multiple triggers that overlap (Page View + DOM Ready on the same page = double-fire).
- Tags fired by a "Page View - All Pages" when they should be scoped to specific paths (audit the URL filter in the trigger config).
- Triggers with no associated tag (orphans — clean up).
3. Trigger duplicates
Sort triggers by name. Look for "Page View 1", "All Pages", "All Pages (1)" — duplicates that crept in over time. Same logic, different name. Consolidate to a single canonical trigger and update tags. Test in Preview after consolidating.
4. Variable usage map
Variables → for each, check Where Used. Variables with zero references are dead — remove them. Variables used in 30+ tags are usually User ID or Customer ID and are load-bearing — note them as critical-path.
5. Lookup-table sanity
Lookup variables (especially "Page Path → Page Type" or "URL → Section") rot the fastest because they hardcode URL patterns. Check that every entry still maps to a valid path on the live site, and that the default (the empty bottom row) is what you want. A lookup table with 47 entries and a default of "checkout" is a recipe for misclassified events.
6. Built-in vs. custom data layer pushes
Open the live site, watch the dataLayer in the console:
window.dataLayer
For each event push, find the tag/trigger that responds to it. gtm.js, gtm.dom, gtm.load are GTM internals — those are fine. Custom events like purchase, add_to_cart, lead_submit: confirm the tag schema matches what the dataLayer is pushing. Mismatched parameter names are the most common silent breakage. A tag listening for value when the dataLayer pushes transaction_value fires but reports zero.
7. Consent Mode + blocked tags
If consent mode is on (and increasingly it should be), open Preview → check the "Consent" tab. Tags blocked by missing consent show as not fired (consent declined). Two things to verify:
- Tags that SHOULD only fire with consent (analytics, ad-platform pixels) are correctly blocked when consent is declined.
- Tags that DON'T need consent (functional, security) aren't being accidentally blocked.
The combinatorial explosion of consent state × tag list is where 80% of post-GDPR consent issues live.
8. Server-side tags (if applicable)
If the container has a gtm-server companion, audit it separately:
- Each server-side tag should map to a client-side relay tag (the request passes through the Server container).
- Check
gtm-server's log explorer for tags that have errored in the last 7 days. - Confirm the server container's URL is on a same-domain or first-party subdomain (cookies + consent reasons).
If you're not sure why server-side GTM exists in this client's setup, the server-side tracking decision post covers when it pays back and when it's overhead.
9. Publish frequency
Versions → look at the publish history. Two patterns to flag:
- Long gaps then a flood (e.g. 6 months idle, then 14 publishes in a week) — often means a fire-drill rebuild that didn't get documented.
- High-frequency micro-publishes (10+ per day) — usually means someone was debugging in production, which is risky.
Healthy: 1-3 publishes per week with descriptive version notes.
10. Permissions audit
Admin → User Management:
- How many people have Publish rights? Should be tightly scoped.
- Are there contractors / former agency-of-record people who still have access? Off-board them now.
- Is there a service account integrated with anything (Zapier, custom backend)? Document it.
11. Container-level events to GA4
If GA4 is configured via a GA4 config tag in this container, verify:
- The GA4 measurement ID matches the property the client expects.
- Event parameters in the config tag include the standard recommended ones (
page_location,page_referrer,page_title,language). - No PII (email, phone, full name) in any event parameter — Google's policy disallows this and silent rejections are hard to debug.
12. Cross-domain + sub-domain settings
If the client has a multi-domain setup (e.g. app.example.com + marketing.example.com + checkout.example.com):
- GA4 cross-domain tracking is configured under GA4 Data Streams → "Configure tag settings" → "Configure your domains."
- Cookie domain in the GA4 config tag should match (usually
auto). - Linker parameter is being passed on cross-domain links (check Network tab on a click).
Misconfigured cross-domain is why analytics shows the same user as two different sessions across the funnel.
After the audit
Three deliverables to the client:
- A typed inventory — every tag, every trigger, every variable, with status (working / broken / unused / questionable). This is exactly what a tracking infrastructure map renders.
- A change log of recommended cleanups, ranked by risk × value. Don't ship them in one giant publish.
- A monthly recurring audit — the difference between a clean container and a polluted one is one quarterly inheritance from a contractor who didn't read the conventions doc. Audit prevents drift.
What this looks like in the tracking map
In Phloz, every GTM container is a typed node. Click the container — see the active tags as child nodes (each typed: GA4 event, Google Ads conversion, Meta Pixel, etc.). Each tag node has a lastVerifiedAt field so you can filter by "haven't checked in 30+ days." The audit checklist above becomes a saved view, runnable per-client in 15 minutes once it's been done the first time.
The takeaway
GTM containers entropy fast. The 12-point audit is the same shape every time — once a quarter per client, ~45 minutes per container, results documented as part of the client's operational record. The hard part isn't the technique; it's making it part of the recurring rhythm.
Inherit a clean container and you ship strategy in week 1. Inherit a polluted one and you spend week 1 figuring out what 14 deprecated tags do before you can do real work. Audit catches that gap before it becomes the engagement.