Free scan About Support Contact
Get Velo

Already have an account? Log in

Can I move a script into Google Tag Manager to make it consent gated?

Consent Mode 29 August 2026· 7 min read
The Velo mascot moves a loose script into a tidy container while a ghost looks on

All posts

Yes for most vendor scripts. Move the snippet into a Google Tag Manager tag and delete the copy in the head. The script is then blocked until consent: the tag runs only when its trigger fires and its consent settings allow it. The exception is any script that must act before the page renders or see the very first request.

Start with a scan.

The free scan reads your homepage HTML and public Tag Manager container, and lists the tracking tags it finds.

Free · No signup · HTML and public GTM checks

What does moving a script into Google Tag Manager change?

A script hardcoded in the head runs the moment the parser reaches it. There is no gate in front of it, which is why a vendor snippet sitting in the head is the most common reason a site sets cookies before the visitor has agreed to anything.

Move it into a container and it stops being markup and becomes a tag. It now runs when a trigger fires it and when its consent settings allow it to proceed. That is exactly what you wanted. It is also the part people underestimate: you have not only added a gate, you have changed when the script runs. For most vendors that is harmless. For a few it is the whole problem.

Which scripts cannot move into the container?

Three shapes fail the move, all for one reason: a container runs after the page has already started, and these scripts need to act before that.

  • Anything that has to run before render. Personalisation and A/B testing scripts rewrite the page before the visitor sees it. Behind a trigger they arrive after first paint, so the visitor watches the original content appear and then change. That flicker is the exact thing the script existed to prevent, so moving it does not gate it, it breaks it.
  • Anything that has to see the first request. Some bot and fraud tools are built to observe the earliest moment of a visit. Fired from a container they see a page that has already been open for a while, and their answer changes accordingly.
  • The consent layer itself. You cannot gate the thing that collects consent behind consent. The banner has to be able to run before any decision exists, which is why it belongs in the head, and why it is one of the few scripts that genuinely is strictly necessary.

When a script cannot move, leave it where it sits and gate it there: your consent tool blocks it in the page, by category. Same outcome, different mechanism, and the category question is covered in what consent category a script should go in.

The test is timing, not category.

StaysIn the head
  • Must run before render: personalisation, A/B testing
  • Must see the first request: some bot and fraud tools
  • The consent layer itself
MovesInto the container
  • Analytics
  • Advertising and retargeting pixels
  • Session recording
  • Chat and support widgets
  • Embedded players and social widgets

A container cannot fire anything early enough to beat first paint.

The question is not what the script does, it is when it has to act. Anything that must run before the page renders stays in the head and gets gated there instead.

How do you move a script without leaving a copy behind?

  1. Remove it from the head first

    Not last. A script that exists in both places runs twice, and the copy in the head runs ungated, so the container tag you just built proves nothing. This is the most common way a migration ends up looking finished while changing nothing.

  2. Rebuild it as a tag

    A custom HTML tag holding the vendor's snippet, or the vendor's own template if the gallery has one. Prefer the template: it exposes the vendor's parameters as proper fields and usually arrives with its consent settings already declared.

  3. Set the consent settings on the tag, not only the trigger

    In a web tag these live under Advanced Settings, then Consent Settings. There you require additional consent for the tag to fire and name the consent types it depends on. Google documents the panel in its Tag Manager consent overview. The trigger is the other control. You want both, for the reason in the next section.

  4. Publish, then test on the live page

    Preview mode runs with the container's debug wiring and, very often, a consent state you clicked through yourself a minute earlier. It is good for confirming a tag exists. It is not evidence about what a real visitor gets.

  5. Check both paths in the page, not in the container

    On the deny path the vendor should be absent from the DOM and absent from resource timing, so nothing was injected and nothing was fetched. On the accept path it should appear in both. Resource timing is the one that matters: if the vendor host was fetched, it was fetched, whatever the container reported.

One exception to step three, and it matters: everything above is guidance for third party vendor scripts. Google's own tags carry built in consent checks, so an Additional Consent check on a GA4 or Google Ads tag blocks it outright instead of shaping it. Whether to block Google's own tags until someone consents covers why, and how to read what your container is actually doing.

If the vendor still shows up on the deny path after all that, the script you moved is probably not the one firing. Something else on the page is loading it, which is a different fault with its own diagnosis in why the Facebook pixel still loads when your tags are blocked.

Is a consent check on the tag the same as a trigger that waits?

This is the distinction that decides whether the gate actually holds. A trigger controls when a tag is considered. Consent settings control whether it is allowed to run once it has been considered.

Gate with a trigger alone and you have covered the first page load and nothing else. A tag whose trigger is a later event, a click, a form submission, a route change in a single page app, is considered again the moment that event happens. With no consent requirement on the tag, it runs then, whatever the visitor chose.

Put the requirement on the tag as well and it holds wherever the tag is reached. The trigger says when to think about firing; the consent settings say whether firing is permitted. Together the gate becomes a property of the tag rather than of one route into it.

Many consent tool guides gate with a custom event trigger conditioned on the tool’s consent cookie, sometimes with a blocking exception on top. That works, but it is still a trigger condition: it holds only if every trigger that can reach the tag carries it. A consent requirement on the tag covers every route.

Can a script you call strictly necessary skip the gate?

The last constraint is not technical. A vendor is sometimes argued as strictly necessary so that it can run before consent, and occasionally that argument is sound. What matters is that the site's own consent notice and cookie table say the same thing, word for word.

If your notice lists a vendor under a category the visitor is invited to refuse, the tag has to honour that refusal. A script running ungated while the page says the visitor may decline it is a contradiction a regulator can read off your own site.

Velo blocks by category in the page and passes the same decision into the container, so the visitor’s choice reaches the page and the container at the same time.

Common questions

What people ask about this topic.

Can I move a script into Google Tag Manager to make it consent gated?

Usually yes. Once the script is a tag rather than markup in the head, it runs only when a trigger fires it and its consent settings allow it. The exceptions are scripts that must act before the container can reach them: A/B testing and personalisation that run before render, tools built to observe the very first request, and the consent layer itself. Those stay in the head and get blocked there by category.

How do I block a third party script until consent in Google Tag Manager?

Take the snippet out of the page source and rebuild it as a custom HTML tag or the vendor’s template. Under Advanced Settings, then Consent Settings, require the consent types it depends on, such as ad_storage for an ad pixel, and keep a trigger that fires after the visitor’s choice. Then decline on the published page and confirm the vendor host never appears in resource timing.

Which scripts should stay in the site head?

The ones whose job depends on running early: personalisation and A/B testing scripts that rewrite the page before first paint, some bot and fraud tools watching a visit’s first moment, and the consent banner itself. Analytics, advertising pixels, session recording, chat widgets and embedded players move into a container cleanly.

Is a consent check on the tag the same as gating it with a trigger?

No. A trigger controls when a tag is considered; consent settings control whether it may run once considered. Gate only with a trigger, and a tag fired by a later click, form submission or route change runs whatever the visitor chose. A consent requirement on the tag makes the gate hold on every route.

How do I check that a script is really blocked before consent?

Test on the published page, not in preview mode, which runs with debug wiring and often your own consent state. On the deny path the vendor should be absent from the DOM and from resource timing; on the accept path it should appear in both. If the vendor host was requested, it was requested, whatever the container reported.