Skip to content
← Back to Blog

What's Hiding in Your Custom HTML Tags

Custom HTML tags run with full page privileges and nobody audits them after publishing. Here's how to classify what's in them and what to look for.

What's Hiding in Your Custom HTML Tags

Every GTM container has Custom HTML tags. They're the escape hatch when Google's tag templates don't cover what you need: a vendor gives you a script snippet, you paste it into a Custom HTML tag, wire it to a trigger, and publish. It works, and nobody looks inside it again.

Custom HTML tags execute with full page privileges. They can access the DOM, read and write cookies, interact with localStorage, make network requests to any domain, and push data into the dataLayer. Unlike custom tag templates (which run in a sandboxed environment with restricted APIs), Custom HTML has no restrictions. The browser treats the code inside a Custom HTML tag the same as any other script on the page, which makes Custom HTML the most powerful and the least visible part of most GTM containers.

Custom HTML in a typical container

In a typical B2B container with 20-40 tags, Custom HTML accounts for 30-60% of the total. The rest are native Google tag types (GA4, Ads, Conversion Linker) or third-party templates from the Community Template Gallery. The native and template tags are constrained by their type, limited to what their template permits with APIs restricted to specific functions. Custom HTML tags are constrained by nothing.

When you look inside them, the code falls into recognizable categories. Some load external scripts from vendor CDNs: a single <script src="https://vendor.com/tracker.js"> that pulls in a third-party library. Some push data to the dataLayer for other tags to consume, reading page information and restructuring it as events. Some read cookies or URL parameters and write them to hidden form fields for cross-domain tracking. Some manipulate the DOM to inject chat widgets, modify navigation links, or track scroll behavior. Some do several of these things at once, combining data collection with external network requests in a single tag.

The ratio matters because it determines how much of the container's behavior is transparent. A native GA4 Event tag does exactly what its configuration says, and you can read the tag settings to know what event name, parameters, and measurement ID it sends. A Custom HTML tag that sends data to the same GA4 property might also read 14 cookies, check 3 URL parameters, and push enriched data to an entirely separate endpoint. The configuration doesn't describe the behavior. The code does.

The problem isn't that these behaviors are inherently wrong, because plenty of Custom HTML tags do exactly what they're supposed to do. The problem is that nobody audits them after they're published. The tag fires, the vendor confirms data is arriving, and the ticket closes. Months or years later, the tag is still running code that nobody in the organization has reviewed.

Obfuscated code in GTM tags

Some Custom HTML tags contain code that's deliberately difficult to read. atob() decodes base64 strings at runtime, while String.fromCharCode() constructs URLs character by character. These techniques make it impossible to tell what a tag does by reading the source, because the destination URL, the data being sent, and the purpose of the tag are all hidden behind encoding layers.

A documented campaign from late 2023 used GTM Custom HTML tags as the delivery mechanism for payment skimming malware. The attacker registered 40 domains through a single hosting provider, and the malicious Custom HTML tag selected two domains at random on each page load, making the destination unpredictable. The code was obfuscated with multi-layer encoding that would break if any character was modified, preventing casual inspection from revealing its purpose.

This isn't a theoretical risk. Spider AF reported that 78% of web skimmer incidents in 2024 began with a tag designed to look like a standard analytics or marketing pixel. The tag passed initial review because it looked like every other vendor snippet. The British Airways breach in 2018, which compromised hundreds of thousands of payment cards, used a similar vector: injected JavaScript that mimicked legitimate page functionality while exfiltrating form data.

Classifying Custom HTML by behavior

Not every Custom HTML tag with atob() is malicious. Some vendors encode their configuration parameters for legitimate reasons, whether protecting API keys, reducing script size, or preventing casual modification. The signal isn't any single technique but the combination of encoding with external network requests to unfamiliar domains, especially when the destination URL is constructed dynamically rather than written in plain text.

The challenge is scale. A container with 15 Custom HTML tags might contain 400 lines of third-party JavaScript across all of them. Reading that code requires familiarity with DOM APIs, cookie handling, and network request patterns. Most GTM administrators are marketers or analysts, not JavaScript developers, and the code was pasted in by someone who left two years ago. Nobody has opened the tag since.

A practical classification starts with what the code does, not whether it looks suspicious. Classifying by behavior makes the review manageable even for someone who doesn't read JavaScript fluently.

Loads an external script. The tag creates a script element and sets its src to a vendor CDN. This is the most common pattern and usually legitimate. The audit question is whether the domain is recognized, whether the script is loaded over HTTPS, and whether the tag has a consent gate.

Pushes data to the dataLayer. The tag reads page information (URL, form fields, element attributes, cookie values) and pushes it as a dataLayer event. This data becomes available to every other tag in the container. The audit question is what data is being pushed and whether it includes personally identifiable information.

Reads or writes cookies. The tag interacts with document.cookie directly. In a post-consent world, any cookie operation should be gated on the appropriate consent type. The audit question is whether the tag respects consent state and whether the cookie is necessary.

Manipulates the DOM. The tag adds, removes, or modifies page elements. This is common for A/B testing, chat widgets, and dynamic content insertion. The audit question is whether the manipulation is visible to the user and whether it could affect page performance.

Uses eval or Function constructor. The tag executes dynamically constructed code. This is almost never necessary for legitimate measurement or marketing purposes, and it's the strongest signal that the tag deserves manual review.

Contains ad pixels. The tag fires a tracking pixel (image request or sendBeacon call) to an advertising platform. This should have consent enforcement, and if the platform has a native GTM template, migrating to the template gives you built-in consent and removes the Custom HTML risk.

Code review as the core of a container audit

Knowing what each Custom HTML tag does is the prerequisite for every other assessment. Consent requirements depend on whether the tag processes personal data. Security exposure depends on whether the tag makes external requests to domains outside the organization's control. Performance impact depends on whether the tag loads synchronous scripts that block page rendering. PII risk depends on whether the tag reads form data, cookies, or URL parameters containing user identifiers.

Most containers have between 5 and 25 Custom HTML tags. Reading each one takes 2-5 minutes for straightforward code, longer for obfuscated scripts. For a consultant scoping an engagement or an in-house team inheriting a container, this is the part of the audit where the discoveries happen. The native Google tags are predictable, with their behavior defined by type and configuration, and you can audit them from the GTM interface without reading code. Custom HTML requires reading the code itself.

The typical finding pattern goes like this: two or three Custom HTML tags are doing exactly what they should, one is loading a vendor script that now returns a 404 because the vendor changed their CDN and nobody updated the tag, one is pushing more data to the dataLayer than anyone realized, and one is a mystery. The mystery tag has no documentation in the tag notes, the vendor name doesn't appear in the organization's marketing stack, and the person who added it no longer works there. That mystery tag is either a legitimate tool that predates the current team's tenure, or it's something that shouldn't be there. The only way to know is to read the code.

Custom HTML is where vendors hide tracking, where legacy code accumulates, where PII gets exposed, and where the occasional malicious script lives undetected. An audit that skips Custom HTML code review is an audit that misses the most consequential findings.

Audit your GTM container

TagManifest gives you an instant health score and prioritized fixes.

Scan Your Container