Audit a GTM container export: find paused tags, Custom HTML, and missing consent settings
Paste an exported Google Tag Manager container JSON into a free client-side explorer to audit paused tags, Custom HTML, missing consent, and unused config.
last verified 2026-07-18
Every Google Tag Manager container older than a year carries sediment: tags someone paused during an incident and never revisited, Custom HTML snippets a long-gone agency pasted in, triggers that no tag fires on, and Data Layer Variables reading keys the site stopped pushing two redesigns ago. The GTM interface shows you one entity at a time, which is exactly the wrong shape for answering the questions an audit actually asks: what is in here, what is dead, and what would fire without a consent check?
The GTM Container Explorer answers those questions from the artifact you already have: the container export (Admin → Export Container in GTM). Paste the JSON, click Explore container, and you get a flat, flagged inventory of every tag, trigger, and variable, plus an instant audit and the list of dataLayer keys the container expects your pages to push. It runs entirely in your browser — the JSON is parsed locally, nothing is uploaded, and the page works offline.
What it does
- Parses the official export format. It reads the
containerVersionobject of a GTM export — thetag,trigger,variable, andbuiltInVariablearrays — and also tolerates a barecontainerVersionpayload pasted on its own. A raw publishedgtm.jsfile is minified JavaScript and is explicitly out of scope; this tool reads the clean exported JSON. - Summarizes the container. Stat cards show the container name plus counts of tags, triggers, variables, and enabled built-in variables.
- Runs a five-check audit. Findings are sorted warnings-first, each with a stable code (like
dangerous-customHTML), a message naming the entity, and a short reference note explaining why it matters. A clean container gets an explicit “no issues found in the checks this tool runs” state rather than silence. - Extracts the dataLayer contract. Every Data Layer Variable in the container is mined for the
key it reads, giving you the list of
dataLayer.push()fields your page code must supply. - Flags every row inline. The tag table chips each tag with
paused,Custom HTML,consent setorno consent, and its trigger count. Triggers getin useorunusedplus their ID; variables getreferencedorunused, with adata layerchip on Data Layer Variables.
How it works
The page is a UI shell over a small dependency-free engine (gtm-container.js, exposed as
GTMX) that does all parsing and auditing. There is no server component.
Parsing. parseContainer accepts the export JSON as a string or object and never throws:
malformed JSON or a non-container object simply normalizes to an empty shape, and the UI tells
you it found no tags, triggers, or variables. GTM’s export encodes parameters as arrays of
{ type, key, value, list, map } objects; the engine flattens each into a plain { key: value }
object, recursing into list (to arrays) and map (to objects) so nested settings like field
maps survive. Tag records keep their paused state (normalized from true or "true"), their
consentSettings, and their firingTriggerId / blockingTriggerId arrays; triggers keep their
filter and customEventFilter conditions.
Reference resolution. GTM wires variables into everything else through {{Variable Name}}
placeholders. The engine walks every string value in every tag, trigger, and variable — parameters
plus trigger filters and custom event filters — with the pattern \{\{([^{}]+)\}\} and collects
each referenced name. That set powers both the referenced/unused chips and the audit.
The five audit checks:
paused-tag(info) — any tag withpaused: true. Paused tags never fire; the finding asks you to confirm it is intentional and not a forgotten kill-switch.dangerous-customHTML(warn) — any tag whose type ishtml. Custom HTML runs arbitrary JavaScript in the page, so each one is flagged for manual review.missing-consent-check(warn) — a tag counts as consent-configured only if itsconsentSettingshasconsentStatus: "needed"or a non-emptyconsentTypearray. Anything else (including GTM’s defaultnotSet) is flagged as potentially firing before or without a consent decision.unused-variable(info) — a declared variable whose name never appears as{{name}}anywhere in the scanned parameters and filters. Likely dead config.unused-trigger(info) — a trigger whose ID is not in any tag’s firing or blocking trigger lists. It does nothing.
dataLayer keys. Data Layer Variables have type v in the export, with the key they read
stored in their name parameter (dot paths like ecommerce.transaction_id included). The engine
deduplicates these into the key list the explorer displays — effectively the container’s read-side
dataLayer contract.
How to use it
- In GTM, go to Admin → Export Container, pick the version you want to audit (usually the live one), and download the JSON.
- On the Paste container tab, paste the file’s contents into the textarea and click Explore container (or press Ctrl/Cmd+Enter). The status line confirms how many tags, triggers, and variables were parsed.
- The Explore & audit tab opens automatically. Read the audit first: warnings (Custom HTML, missing consent) before info findings (paused tags, unused triggers and variables).
- Cross-check the Data layer keys referenced list against what your pages actually push — the dataLayer Validator handles the page side of that comparison.
- Scan the tag, trigger, and variable tables for
paused,no consent, andunusedchips to build your cleanup list.
No container handy? Load sample fills in a realistic export: a GA4 event tag with consent
configured, a paused legacy Custom HTML pixel, a Floodlight tag with no consent settings, an
orphaned click trigger, and an unused user.email Data Layer Variable — enough to light up every
audit check at once.
Limitations
- Exports only, not live behavior. The tool reads configuration, not what actually happens in
a browser. A published
gtm.jscannot be pasted, and the audit cannot tell you whether a tag really fired, what it sent, or what your CMP did — that is live-page territory. - Five checks, not a full governance review. The audit covers paused tags, Custom HTML, consent settings, unused variables, and unused triggers. It does not review tag sequencing, naming conventions, folder hygiene, or the contents of Custom HTML — it flags those tags precisely because they need human eyes.
- Consent check is structural. A
consent setchip means the export declaresconsentStatus: "needed"or lists consent types; it does not prove the consent wiring is correct end to end, and a compliant setup built entirely on a consent-blocking architecture outsideconsentSettingswill still showno consent. - Reference scan covers parameters and filters. “Unused” means the variable’s
{{name}}never appears in any tag, trigger, or variable parameter, trigger filter, or custom event filter. References living elsewhere are not counted. - Only the four core arrays are parsed. Custom templates, folders, zones, and server-container clients in the export are ignored by the normalizer.
- Client-side, but treat the file as sensitive. Nothing is uploaded, yet container exports can contain API secrets or internal URLs inside tag parameters — only paste containers you are authorized to inspect.
FAQ
Is my container JSON uploaded anywhere? No. Parsing and auditing run entirely in your browser; there is no server, no upload, and the page works offline. The privacy note still applies: exports can embed secrets, so handle the file like the sensitive config it is.
Can I paste a gtm.js URL or the script itself?
No. Published gtm.js is minified JavaScript, not the export format. Use Admin → Export Container
in GTM to get the JSON this tool reads. To see what a live page’s tags actually load and send, use
a live-site audit like Tag Reaper instead.
Why is a tag flagged “no consent” when we have a CMP?
The check looks at the tag’s consentSettings in the export. If additional consent is not
configured there (GTM’s notSet default), the tool warns, because nothing at the tag level gates
firing on a consent decision. Your CMP may still block it upstream — the finding is a prompt to
verify, not a verdict.
What counts as an “unused” variable or trigger?
A variable is unused when its {{name}} reference appears nowhere in the container’s scanned
parameters and filters; a trigger is unused when no tag lists its ID under firing or blocking
triggers. Both are info-level findings — dead config to clean up, not breakage.
Does it work for server-side (server container) exports?
Partially. The parser reads whatever tag, trigger, and variable arrays it finds, but
server-container clients and transformations are not parsed, and the audit heuristics were built
around web containers.
Related
- GTM Container Explorer — the tool itself, and the full tools index.
- Background: the Google Tag Manager guide covers the concepts this tool inventories — tags, triggers, variables, and consent settings — and how web tracking works sets the wider context.
- The dataLayer keys list pairs with the dataLayer Event Spec and the events and schemas guide; validate the page side with the dataLayer Validator.
- Consent findings lead naturally to the Consent Checker and the GPC and consent primer. On Adobe? The Launch Explorer is the Adobe Launch counterpart.
- This explorer audits your container’s configuration once. For continuous monitoring of what those tags actually do on the live site — every request, pixel, and third party — run it through Tag Reaper.
- TagForge is pre-launch: it turns container audits like this into managed, ongoing tag governance, and subscribers at /#subscribe get first access.