Hold a Visitor ID Script Until Consent: OneTrust, Cookiebot, CookieYes
To hold a visitor-identification script until consent, the consent platform (CMP) must keep it from running until the visitor opts into its category. OneTrust sets the script's type to text/plain with an optanon-category class; Cookiebot uses type="text/plain" with a data-cookieconsent attribute; CookieYes uses a data-cookieyes attribute or a Script URL Pattern. Each CMP holds only what it is wired to hold. A script that runs before the CMP's own blocking script, a Google Tag Manager tag without the vendor's documented consent gate, or a script the automatic blocker has not matched can still fire before consent.
OneTrust and Cookiebot hold a script the same way. The script tag gets type="text/plain", so the browser does not run it. When the visitor consents to the right category, the CMP enables it. OneTrust names the category in an optanon-category class. Cookiebot names it in a data-cookieconsent attribute. CookieYes uses a data-cookieyes attribute on the script tag, or a Script URL Pattern you add in its Cookie Manager. Each CMP holds only what you marked up, what its scan found, or what is on its own list of known trackers.
Visitor-identification installs usually break at the install route, not the markup. OneTrust and Cookiebot both say their automatic-blocking script must load first; a script that runs before it is not held. Tags fired from Google Tag Manager have their own documented gate in all three platforms. Set that gate. Do not assume page-level blocking covers the tag.
This guide gives the exact markup, the automatic-blocking rules, and the GTM gate for each platform, side by side, plus a DevTools test that proves the hold works. It does not cover whether a given tag should wait for consent, or which category it belongs in. That call belongs to the site's privacy owner, and the consent-mode decision tree covers it.
How do OneTrust, Cookiebot and CookieYes compare?
| OneTrust | Cookiebot | CookieYes | |
|---|---|---|---|
| Manual hold markup | type="text/plain" + class="optanon-category-C0002". Use the category ID from your own account; OneTrust's examples use C0002 for Performance and C0004 for Targeting |
type="text/plain" + data-cookieconsent="preferences" / "statistics" / "marketing" (combinable) |
data-cookieyes="cookieyes-analytics" / "cookieyes-performance" / "cookieyes-functional" on an external script with a src |
| Automatic blocking | Uses scan results: controls scripts whose cookies have a non-essential category and a correct source URL. An optional setting maps scripts from a Common Host List (doubleclick.net, facebook.net, linkedin.com and others) to Targeting | Scan-based: identifies scripts by "consent checksums, files, paths and keyword matchings." Some known cookies are blocked at once; full coverage follows the first scan, which can take up to 24 hours | Recognizes "most of the commonly used third-party service scripts." Anything else needs a Script URL Pattern or the attribute |
| What must load first | For Auto-Blocking: OtAutoBlock.js as the first script in <head>, synchronous, added directly to the page, not through a tag manager or injector |
The uc.js loader as the very first script. Automatic mode adds data-blockingmode="auto" and removes async |
Not stated in the docs read for this guide |
| GTM-fired tags held via | A Custom Event trigger on OneTrustGroupsUpdated, conditioned on OnetrustActiveGroups or the OptanonConsent cookie, used as the tag's firing trigger |
GTM's Additional Consent Checks ("Require additional consent for tag to fire") with the mapped consent type, plus a cookie_consent_update Custom Event trigger in place of All Pages |
A Custom Event trigger on cookie_consent_update, conditioned on a 1st Party Cookie variable that reads cookieyes-consent |
| Escape-hatch attribute | data-ot-ignore: Auto-Blocking never blocks the script, whatever its category |
data-cookieconsent="ignore": the docs warn that any element with it "can set tracking without the user's consent" |
None documented in the pages read |
| What the vendor documents the escape hatch for | A script or resource that Auto-Blocking blocks unexpectedly. OneTrust's example is the GTM container snippet itself | A script that must run regardless of consent, where you already make sure its cookies are set only after consent by another method | Not applicable |
| Documented browser check | GTM Preview: after you change choices in the Preference Center, the category's tags show under Tags Not Fired for OneTrustGroupsUpdated |
Private window, DevTools Application > Cookies: delete cookies, reload, browse without touching the banner, then opt in and compare | Private window or cleared cookies, DevTools Application > Cookies: no cookies before consent; they appear after you accept and reload |
| Source, read 2026-09-26 | Auto-Blocking, Preventing Auto-Blocking, Client-Side Cookie Management, GTM integration | Manual blocking, Automatic blocking, Disable auto-blocking, GTM deployment | Automatic blocking, Implement prior consent, GTM blocking |
A visitor-identification script that is meant to wait for consent fits neither documented use of an escape hatch. If one is on the tag, remove it.
Google's consent mode comes up in all three setups. Google's developer guide describes it as setting a default consent state and updating it when the visitor interacts with the banner; Google tags act on that state. The guide does not describe it as a way to hold a third-party script. Cookiebot's GTM guide adds that tags with built-in consent checks (Google Ads, Analytics, Floodlight) load regardless of consent. The Google Tag Manager control that holds a tag is Additional Consent Checks, a setting on each GTM tag.
How do you hold a script in OneTrust?
OneTrust calls its manual method JavaScript Type Re-Writing, and recommends it for scripts in the page source. Set type to text/plain and add a class with the category ID. The IDs must match the categories in your own OneTrust account, which can include custom IDs. Find them under Categorizations.
<!-- Held until the visitor consents to this category -->
<script type="text/plain" class="optanon-category-C0004"
src="https://cdn.vendor.example/tag.js"></script>
When the visitor consents to that category, OneTrust changes type to text/javascript. To require several categories, join the IDs: class="optanon-category-C0002-C0003-C0004". OneTrust's docs say none of the cookies in those categories will be set unless the visitor consents to all of them.
Auto-Blocking does the same rewrite automatically, from your scan results. It controls a script only when the scanner found its cookie, the cookie has a non-essential category, and the cookie has the correct source URL. Strictly Necessary and Unknown cookies are excluded. OneTrust also notes that the scanner may miss a tag triggered by a form submission, scroll depth or a timing delay, and that cookies set by inline document.cookie code are not supported. Use type re-writing for those.
The install route to watch is the Auto-Blocking script itself. OneTrust says it must be added directly to the page as the first script in <head>, loaded synchronously. If a tag manager loads it, tags can fire before it completes.
Which category the script goes in is the privacy owner's decision, not a technical default.
How do you hold a script in Cookiebot?
Cookiebot's manual method sets type to text/plain and adds a data-cookieconsent attribute: "preferences", "statistics", or "marketing". Values can be combined, for example "statistics, marketing".
<!-- Held until the visitor consents to Statistics -->
<script type="text/plain" data-cookieconsent="statistics"
src="https://cdn.vendor.example/tag.js"></script>
Cookiebot says manual markup holds the script even if the Cookiebot script fails to load.
Automatic blocking is a setting on the loader script: data-blockingmode="auto" and no async, placed as the very first script on the page. The scanner identifies cookie-setting scripts by "consent checksums, files, paths and keyword matchings." Some known cookies are blocked as soon as the script is on the site; full coverage waits for the first scan, which can take up to 24 hours. If a script sets cookies in more than one category, it stays blocked until every one of those categories has consent.
The install route to watch is Cookiebot deployed through GTM. Cookiebot's GTM guide says scripts inserted directly into the site template then need manual markup. For tags fired from GTM itself, use the GTM gate below.
How do you hold a script in CookieYes?
CookieYes's manual method puts data-cookieyes on the script tag. Its prior-consent guide names three values: cookieyes-analytics, cookieyes-performance, and cookieyes-functional. Its list of categories also includes advertisement, but the guide gives no attribute value for it.
<!-- Held until the visitor consents to Analytics -->
<script async data-cookieyes="cookieyes-analytics"
src="https://cdn.vendor.example/tag.js"></script>
The guide says the attribute is for external scripts that use a src. For a script you cannot add the attribute to, it gives a listener script. On cookieyes_banner_load, the listener checks whether the category is already accepted. If not, it waits for cookieyes_consent_update and injects the script only when the category is in the accepted list.
For automatic blocking, CookieYes recognizes "most of the commonly used third-party service scripts." For anything else, add a Script URL Pattern: in Cookie Manager, open the category, click + Add Cookie, and enter the pattern, for example the vendor's domain. CookieYes also notes that in its Info consent type, cookies are not blocked at all.
Nothing in CookieYes's docs assigns a visitor-identification tag to a category. That is the privacy owner's decision too.
How are GTM-fired tags held, per vendor?
A tag added inside Google Tag Manager fires on GTM's own triggers. Each vendor documents a GTM-side gate for it. Set that gate even when page-level blocking is on.
- Cookiebot uses GTM's Additional Consent Checks. In the tag's Advanced Settings > Consent Settings, choose "Require additional consent for tag to fire." Add the consent type Cookiebot maps to the category: Statistics →
analytics_storage; Marketing →ad_storage,ad_user_data,ad_personalization. Google says the tag then fires only if all listed consent types are granted. Cookiebot also tells you to replace the tag's All Pages trigger with a Custom Event trigger oncookie_consent_update. On All Pages, the tag does not fire on the page where a first-time visitor consents. The Cookiebot CMP tag itself goes on the Consent Initialization - All Pages trigger, which Google says fires before all other tags. - OneTrust's GTM integration article gates tags with OneTrust category triggers. Create a Custom Event trigger on
OneTrustGroupsUpdatedwith regex matching on. Fire it whenOnetrustActiveGroupsmatches,C0002,, or whenOptanonConsentcontainsC0002:1, using your own category ID. Replace the tag's All Pages trigger with it. OneTrust covers Google Consent Mode in a separate article, which this guide does not use. - CookieYes also uses a Custom Event trigger. Create a 1st Party Cookie variable that reads
cookieyes-consent. Create a trigger on the eventcookie_consent_updatewith regex matching on (use.*if the account's Support GCM option is off). Set it to fire on Some Custom Events when the variable containsanalytics:yes, then make it the tag's trigger.
The bypass to look for is the same in all three: a GTM tag on a plain All Pages trigger with none of these gates. GTM fires it on page load, before the visitor touches the banner, unless page-level blocking happens to catch it. Do not rely on that. Install Visitor Identification With Google Tag Manager: The QA Checklist catches this before publish.
How do you prove the script is held?
Configuration is not proof. Watch the network and the cookies. Cookiebot and CookieYes both document this check in a private window with DevTools Application > Cookies. OneTrust's GTM article runs the same check in GTM Preview.
- Open a private window, or clear cookies first.
- Open DevTools before loading the page. Filter the Network tab by the vendor's host, and open Application > Cookies for the same origin.
- Load the page. Don't touch the banner.
- Pass: no requests to the vendor host, and no vendor cookies.
- Fail: a request appears, or a vendor cookie is already set.
- Check the Elements panel. With OneTrust or Cookiebot manual markup, a held script still shows
type="text/plain". If the tag showstype="text/javascript"before consent, or has no hold markup at all, the hold is not applied to it. CookieYes's docs do not describe how a held tag looks in the DOM, so for CookieYes rely on steps 3 and 5. - Accept only the category the script belongs to, not "accept all," so you test this one tag. Reload if the CMP's docs call for it.
- Pass: the request now appears and the vendor cookie is present. With OneTrust,
typehas changed totext/javascript.
- Pass: the request now appears and the vendor cookie is present. With OneTrust,
- Withdraw consent for that category and reload.
- Pass: the request does not reappear. OneTrust documents that a withdrawn tag is blocked on later page loads. Treat "no new request" as the pass condition; whether a cookie already in the browser is deleted at once is not covered in the docs read here.
Run this on every page the vendor script should cover, not only the homepage. A narrow GTM trigger can pass on one page and fail on another. How To QA a Visitor Identification Tag After Launch has the full post-launch script this recipe fits into.
What makes a hold fail, and how do you fix it?
| Cause | Symptom in the test | Fix |
|---|---|---|
| Vendor script runs before the CMP's blocking script | Request fires at once, before the banner renders | Put the CMP's loader first, or hold the script with manual markup instead of relying on load order |
| OneTrust Auto-Blocking script loaded through a tag manager | Hard-coded scripts fire before the banner, though Auto-Blocking is on | Add OtAutoBlock.js directly to the page, first in <head>, synchronous |
| Cookiebot deployed through GTM, vendor script hard-coded in the template with no markup | Request fires on load | Add Cookiebot manual markup to the template script |
| GTM tag on a plain All Pages trigger, no vendor gate | Request fires on every load, whatever the consent state | Add the CMP's documented GTM gate (Additional Consent Checks, OneTrust category trigger, or CookieYes custom event trigger) |
| Automatic blocking has not matched the vendor's script | Request fires although auto-blocking is on | OneTrust: categorize the cookie with its correct source URL and republish. Cookiebot: wait for the scan, then classify the cookie. CookieYes: add a Script URL Pattern. Or use manual markup |
Escape-hatch attribute on the tag: data-ot-ignore or data-cookieconsent="ignore" |
Request fires before consent and after a rejection | Remove the attribute and hold the script with category markup |
| Consent model lets scripts run before a click: OneTrust opt-out, Cookiebot implied consent, or CookieYes Info | Request fires on first load, or after the visitor moves to a second page without clicking | Confirm the consent model with the privacy owner; that choice is theirs |
| Script is released by consent but still does not load | No request appears; the console shows a Content-Security-Policy error | The consent hold worked; the page's CSP blocks the script. Check script-src and connect-src with the CSP checker for tracking tags |
Worked example: OneTrust Auto-Blocking loaded through GTM
Hypothetical, for illustration only. A team turns on OneTrust Auto-Blocking and pastes the published script into a GTM Custom HTML tag. The visitor-ID script is hard-coded in the site template, and its cookie is categorized in OneTrust with the correct source URL. The DevTools test in a fresh private window shows the vendor request on load, before anyone touches the banner.
Diagnosis: OneTrust's Auto-Blocking article says the Auto-Blocking script must be added directly to the page, not through an injector or tag manager. It must also be the first script in <head> and load synchronously. OneTrust warns that when a tag manager deploys it, tags can fire before it completes. Here, the template's script ran before GTM loaded the blocker.
Fix: Move the Auto-Blocking script and otSDKStub.js into <head> directly, as the first scripts, without async. Or hold the vendor script with type re-writing and the category ID the privacy owner chose. Then run the test again.
When this does not apply
Server-side identification. If the vendor identifies visitors from server logs or reverse-IP lookups that never load a script or set a browser cookie, there is nothing for a CMP to hold. Whether that workflow needs its own privacy review is a separate question; see the consent-mode decision tree.
Tags that set no cookies. A script may still need to be held even if it writes nothing to browser storage, depending on the site's privacy review. "No cookie" does not mean "no hold required."
Sites with no CMP. None of these mechanisms exist without the platform installed. A site with no CMP has to make and enforce this decision in its own code review.
Claim ledger
| Claim used in this guide | Source checked | How to use it safely |
|---|---|---|
OneTrust's manual method sets type to text/plain with an optanon-category class; a hyphenated multi-category class needs consent to all listed categories |
OneTrust Client-Side Cookie Management doc, read 2026-09-26 | Use the category IDs from your own account |
| OneTrust Auto-Blocking controls only scanned, non-essential cookies with a correct source URL, and the scanner may miss tags triggered by form submission, scroll depth or a timing delay | OneTrust Auto-Blocking doc, read 2026-09-26 | Use type re-writing for anything the scan cannot see |
OneTrust's Auto-Blocking script must load directly on the page, first in <head>, synchronously, not through a tag manager |
OneTrust Auto-Blocking doc, read 2026-09-26 | Check the page source for its position before testing |
data-ot-ignore makes Auto-Blocking never block the element |
OneTrust "Preventing Auto-Blocking" doc, read 2026-09-26 | Never apply it to a visitor-identification script meant to wait for consent |
OneTrust GTM tags are gated with a Custom Event trigger on OneTrustGroupsUpdated, using OnetrustActiveGroups or OptanonConsent |
OneTrust GTM integration doc, read 2026-09-26 | Replace the tag's All Pages trigger with it |
Cookiebot's manual method uses type="text/plain" with data-cookieconsent; valid values are preferences, statistics, marketing, ignore |
Cookiebot Manual cookie blocking doc, read 2026-09-26 | Never use ignore on an identification script meant to wait for consent |
Cookiebot automatic blocking is scan-based, set with data-blockingmode="auto" and no async on uc.js, which must be the first script |
Cookiebot Automatic cookie blocking doc, read 2026-09-26 | Allow up to 24 hours after setup for the first scan before judging coverage |
When Cookiebot is deployed through GTM, template scripts need manual markup; GTM tags use Additional Consent Checks and a cookie_consent_update trigger |
Cookiebot GTM deployment doc, read 2026-09-26 | Put the Cookiebot CMP tag on Consent Initialization - All Pages |
CookieYes's data-cookieyes attribute is for external scripts with a src; the doc names three values |
CookieYes "Implement prior consent" doc, read 2026-09-26 | Use the event-listener method for other scripts |
| CookieYes's Info consent type does not block cookies | CookieYes "Implement prior consent" doc, read 2026-09-26 | Confirm the consent type with the privacy owner |
CookieYes GTM tags are gated with a Custom Event trigger on cookie_consent_update, reading the cookieyes-consent cookie |
CookieYes GTM doc, read 2026-09-26 | Check the account's Support GCM setting first |
| Google's consent mode guide covers setting and updating consent state that Google tags act on; it does not describe holding third-party scripts | Google consent mode developer guide, read 2026-09-26 | Use GTM's Additional Consent Checks, not consent mode, to gate a third-party tag |
| GTM's "Require additional consent for tag to fire" is available in any web tag and fires the tag only if all listed consent types are granted | GTM consent mode support doc, read 2026-09-26 | Use it for a visitor-ID tag when your CMP maps to GTM consent types |
FAQ
Does Google's consent mode hold a visitor-identification script?
No. Google's guide describes it as setting and updating the consent state that Google tags act on. It does not describe holding third-party scripts. For a third-party tag in GTM, use the Additional Consent Checks setting on that tag, or your CMP's documented GTM trigger.
Which consent category should a visitor-identification script go in?
That's the privacy owner's decision, not a technical default. None of the three platforms assigns an identification script to "marketing" or any other category. Record the category chosen and why, separately from the markup.
Does automatic blocking cover a tag added through Google Tag Manager?
Don't rely on it. OneTrust says Auto-Blocking disables new tags in a blocked category when the correct source URLs are present, but it also documents GTM triggers as a separate method. Cookiebot and CookieYes each document a GTM-specific gate for tags fired from GTM. Set the gate your CMP documents, then run the DevTools test.
What's the fastest way to tell if a hold actually works?
Run the DevTools test: a private window, the Network tab filtered to the vendor's host, and a page load without touching the banner. Zero requests is a pass. That one check catches two failures this guide covers: a script that runs before the CMP, and a GTM tag with no consent gate.
The test passed once, but the script still fired on a different page. Why?
A trigger or markup applied to one template does not cover every template the script is on. Run the test again on every page type where the tag should be live, and look for a second, unheld copy of the script in the source.
Sources
- https://support.cookiebot.com/hc/en-us/articles/4405978132242-Manual-cookie-blocking
- https://support.cookiebot.com/hc/en-us/articles/360009063660-Disable-automatic-cookie-blocking-for-a-specific-script
- https://support.cookiebot.com/hc/en-us/articles/360009074960-Automatic-cookie-blocking
- https://support.cookiebot.com/hc/en-us/articles/360003793854-Google-Tag-Manager-deployment
- https://my.onetrust.com/s/article/UUID-cdb2fcad-1640-fdbd-e432-73c5536f0bbd
- https://my.onetrust.com/s/article/UUID-71e7d0a8-03d8-e272-e683-72cadded2ecf
- https://my.onetrust.com/s/article/UUID-518074a1-a6da-81c3-be52-bae7685d9c94
- https://my.onetrust.com/s/article/UUID-301b21c8-a73a-05e8-175a-36c9036728dc
- https://www.cookieyes.com/documentation/automatic-script-blocking/
- https://www.cookieyes.com/documentation/implement-prior-consent-using-cookieyes/
- https://www.cookieyes.com/documentation/block-third-party-cookies-with-google-tag-manager-and-cookieyes/
- https://developers.google.com/tag-platform/security/guides/consent
- https://support.google.com/tagmanager/answer/10718549?hl=en