Skip to main content
Back to Guides
Compliance14 min read

How to Block Scripts Until Cookie Consent

Learn how CookieBeam's automatic blocking engine prevents tracking scripts from firing until consent, plus manual tagging for inline scripts. Covers the 5-layer interception engine, connection-level blocking, drift detection, and copy-paste examples.

Under GDPR and the ePrivacy Directive, tracking scripts can't execute until the user has actively given consent for the relevant cookie category. Showing a cookie banner isn't enough. You need to technically prevent scripts from running until consent is granted.

CookieBeam blocks scripts two ways. The automatic blocking engine works from your site's scan results: the scanner discovers scripts, you categorize them in the dashboard, publish, and the runtime handles everything. No HTML changes needed. For inline scripts or edge cases where you need direct control, manual HTML tagging with data-category attributes gives you that option.

Most sites only need the automatic path. This guide covers both.

Using Google Tag Manager?

Good News

If all your tracking scripts are loaded through Google Tag Manager and you've enabled Consent Mode V2, GTM already gates script execution based on consent signals. You don't need manual script blocking for GTM-managed tags. This guide is for scripts hardcoded directly in your HTML or loaded outside of GTM.

Automatic Script Blocking (Recommended)

The fastest way to block scripts is to let CookieBeam's scanner and runtime handle it. You don't touch any HTML. Here's how it works:

The Automatic Blocking Workflow

1

Scan your site

Run a scan from the CookieBeam dashboard. The headless browser scanner crawls your pages and records every external script, inline script, and network connection it finds.

2

Review and categorize

Open the scanner inventory. CookieBeam auto-classifies most scripts using URL/domain pattern matching (140+ known domains) and inline content analysis (23+ vendor patterns for GTM, Meta Pixel, Hotjar, and others). Review the classifications and fix any that need adjustment.

3

Publish your banner

When you publish, CookieBeam generates a script map derived from your scan results. This map tells the runtime which scripts belong to which consent category. It's specific to your site, not a generic block list.

4

The runtime blocks automatically

On every page load, the CookieBeam runtime intercepts scripts that match the script map and holds them until the visitor consents to the relevant category. Blocked scripts are neutralized (not deleted), queued, and revived in their original order when consent is granted.

No code changes required

The automatic blocking engine intercepts scripts at the browser API level. You don't need to add type="text/plain" or data-category attributes to your HTML. The runtime identifies scripts by their URL and content, matching them against the published script map.

The 5-Layer Interception Engine

CookieBeam doesn't rely on a single blocking technique. The runtime uses five layers of interception to catch scripts regardless of how they're injected into the page:

Interception Layers

1

createElement override

Intercepts creation of script, iframe, img, and embed elements. Proxies the src property setter and setAttribute calls. If the URL maps to an unconsented category in the script map, the element is neutralized before it can load.

2

DOM insertion hooks

Wraps Node.appendChild, Node.insertBefore, and Node.replaceChild. This catches scripts being added to the page no matter how they were created, including by other scripts that build elements dynamically.

3

MutationObserver fallback

Monitors the entire document tree for added nodes and src attribute changes. This is the catch-all: if a script somehow bypasses the first two layers, the observer spots it.

4

Worker blocking

Wraps Worker and SharedWorker constructors to prevent third-party worker scripts from executing without consent. Workers run in a separate thread with full network access, so they can't be left unblocked.

5

Anti-tamper protection

Once a script element is neutralized (type set to text/plain, src removed), the engine locks the type property so it can't be reverted to an executable state. This prevents tracking scripts from detecting they've been blocked and trying to re-enable themselves.

Learning Mode vs Strict Mode

CookieBeam offers two compliance modes that control how unknown scripts (those not in the script map) are handled:

Compliance Modes

AspectLearning Mode (default)Strict Mode
Unknown scriptsAllowed to run while building your inventoryBlocked by default (fail-closed)
Best forInitial setup, when you're still discovering what's on your siteProduction, when your inventory is complete and you want maximum compliance
RiskNew unclassified scripts may run before you categorize themA new script added by your team won't work until you scan and categorize it

Start with Learning Mode while you're setting up. Once your scan inventory is complete and you've verified everything works, switch to Strict Mode for the tightest compliance posture.

Drift Detection

Your site changes between scans. New scripts get added, third-party tags update themselves, your team deploys new features. CookieBeam's drift detection catches these changes in real time.

The runtime monitors every visitor's browser for three types of drift:

Drift detection types
Drift TypeWhat It CatchesHow It Works
Script driftNew scripts loading that weren't in the inventoryCompares loaded scripts against the published script map
Cookie driftUnknown cookies appearing between scansDetects cookies not in the classified inventory
Connection driftNew network connections to unknown third partiesMonitors outbound requests against known connection inventory

This isn't periodic scanning. It's continuous, real-time monitoring running in every visitor's browser. After 5 sightings, a drifted script or connection is auto-promoted into your inventory for review. In Strict Mode, drifted scripts are blocked automatically while you decide how to categorize them.

Connection-Level Blocking

Script blocking stops scripts from running. But tracking can also happen through API calls made by scripts that are already allowed, or by first-party code that phones home to third-party endpoints. CookieBeam's connection-level blocking is a separate enforcement layer that wraps five browser APIs:

Blocked connection APIs
APIWhat Happens When Blocked
<code>fetch()</code>Returns a Response with status 204 and an <code>X-CookieBeam-Blocked</code> header. No network request is made.
<code>XMLHttpRequest</code>Fires an error event without making the request.
<code>navigator.sendBeacon()</code>Returns <code>false</code>. The beacon is never sent.
<code>WebSocket</code>Returns a stub that fires error and close events. No connection is opened.
<code>EventSource</code>Returns a closed stub. No SSE connection is established.

Connection blocking is category-aware, so it respects the same consent categories as script blocking. Same-site requests are always allowed (your own APIs won't break), and you can configure infrastructure host exclusions and ignore rules for specific endpoints.

The engine also re-patches same-origin iframes so scripts inside them can't extract clean native API references to bypass the wrappers.

Connection blocking is a separate toggle

Connection-level blocking is independent from script blocking. You can enable one without the other. Script blocking prevents scripts from loading; connection blocking prevents network requests to unconsented third-party endpoints even if the script that makes them is allowed to run.

Manual Script Tagging

For inline scripts embedded directly in your HTML, the automatic engine can't block them before they run (they're already in the page source). Manual tagging gives you control over these scripts by telling the browser not to execute them until CookieBeam releases them.

You may also prefer manual tagging when you want explicit, visible control over which category a specific script belongs to.

How Manual Blocking Works

CookieBeam exploits how browsers handle the type attribute on <script> tags. When a script has type="text/plain" instead of type="text/javascript", the browser treats it as a data block and doesn't execute it.

CookieBeam scans the page for all script tags marked with a data-category attribute. When a user gives consent for that category, CookieBeam clones the dormant script tag, restores its original type and source, and inserts it back into the page, causing the browser to execute it normally.

The Manual Blocking Lifecycle

1

Page loads with dormant scripts

Your script tags have type="text/plain" and data-category="...". The browser sees them but doesn't execute them.

2

CookieBeam initializes

The CookieBeam runtime scans the DOM and finds all <script data-category="..."> elements, recording which category each belongs to.

3

User interacts with the cookie banner

The user clicks "Accept All" or selects specific categories in the preferences modal.

4

Consented scripts are activated

CookieBeam replaces each dormant script that belongs to an accepted category with a fresh, executable copy. External scripts are loaded sequentially to maintain dependency order.

Required Attributes

To manually block a script, you need exactly two attributes:

Required and optional script blocking attributes
AttributeRequiredDescription
type="text/plain"YesPrevents the browser from executing the script. CookieBeam restores the original type when consent is given.
data-category="..."YesTells CookieBeam which cookie category this script belongs to: necessary, analytics, marketing, or preferences.
data-src="..."For external scriptsUse instead of src for external scripts. Prevents the browser from fetching the file before consent.
data-service="..."OptionalGives the script a human-readable name that appears as a toggle in the preferences modal, letting users control it individually.
data-type="..."OptionalSet a custom script type (e.g. "module") that CookieBeam will restore when the script is activated.

Basic Example: Inline Script

Here's the simplest case, an inline script that should only run when the user consents to analytics cookies:

inline-script-example.html
Copy to clipboard

External Scripts: Use data-src

For external scripts that load a file via the src attribute, replace src with data-src. This prevents the browser from fetching the file before consent is given. Even though type="text/plain" stops execution, many browsers still download the resource when src is present.

external-script-example.html
Copy to clipboard

Always use data-src for external scripts

If you keep the src attribute, the browser may still download the script file (even though it won't execute it). This can trigger network requests to third-party servers before consent, which some Data Protection Authorities consider a GDPR violation. Always replace src with data-src.

Cookie Categories

CookieBeam uses four standard cookie categories. Use the exact lowercase string in the data-category attribute:

Cookie categories and their typical scripts
Categorydata-category valueTypical scriptsConsent required?
NecessarynecessaryLogin, security tokens, payment processing, reCAPTCHANo, always active. No tagging needed.
AnalyticsanalyticsGoogle Analytics, Hotjar, Mixpanel, Plausible, PostHog, AmplitudeYes
MarketingmarketingFacebook Pixel, Google Ads, LinkedIn Insight, TikTok Pixel, Twitter Pixel, CriteoYes
PreferencespreferencesChat widgets (Intercom, Drift), language preferences, UI customisationYes

Ready-to-Use Examples

Below are copy-paste examples for the most commonly used tracking scripts. These are for manual tagging. Replace the placeholder IDs with your own.

Google Analytics (GA4)

google-analytics.html
Copy to clipboard

Facebook Pixel

facebook-pixel.html
Copy to clipboard

Hotjar

hotjar.html
Copy to clipboard

LinkedIn Insight Tag

linkedin-insight.html
Copy to clipboard

TikTok Pixel

tiktok-pixel.html
Copy to clipboard

Google Ads Conversion Tracking

google-ads.html
Copy to clipboard

Per-Service Toggles

When you add the data-service attribute, CookieBeam automatically generates a toggle in the preferences modal that lets users enable or disable that specific service within its category. For example, a user might accept the "analytics" category overall but disable Hotjar specifically.

All scripts with the same data-service name are grouped together. They're activated or deactivated as a unit.

When to use data-service

Use data-service when you have multiple tracking scripts in the same category and want to give users fine-grained control. For example, if you use both Google Analytics and Hotjar (both analytics), adding data-service to each lets users keep one while disabling the other.

Running Cleanup Scripts on Consent Withdrawal

Sometimes you need to run cleanup logic when a user withdraws consent for a category, for example, disabling a Google Analytics property or removing event listeners. You can do this by prefixing the category name with !:

cleanup-example.html
Copy to clipboard

Disable scripts run only once

A data-category="!analytics" script can only fire once, when the user actively switches the category off. If you need logic that runs every time consent changes (e.g. the user toggles analytics on and off multiple times), use the onChange callback instead of script tag blocking.

Script Blocking vs Google Consent Mode

It's important to understand the difference between these two approaches, because they solve the same problem in different ways:

Script Blocking vs Consent Mode

AspectScript Blocking (automatic + manual)Google Consent Mode V2
How it worksPrevents the script from executing entirely until consentLets the script load but sends consent signals so Google tags self-regulate
What scripts support itAny script, works with all vendorsOnly Google tags (GA4, Google Ads, Floodlight)
Data sent before consentNone. Script doesn't even load.Cookieless pings are sent (no PII, used for conversion modeling)
Best forNon-Google scripts (Facebook, Hotjar, TikTok, etc.)Google ecosystem tags loaded via GTM
SetupAutomatic: scan and categorize in dashboard. Manual: add type and data-category to each script.Automatic when using CookieBeam GTM template

Can I use both?

Recommended

Yes, and you should. Use Google Consent Mode V2 for your Google tags (loaded through GTM), and use script blocking for any non-Google scripts hardcoded in your HTML. This gives you the best of both worlds: Google's cookieless conversion modeling plus strict blocking for everything else.

Troubleshooting

Script still fires before consent (automatic blocking)

Make sure the CookieBeam runtime script loads before any other scripts in your <head>. The interception engine needs to install its hooks before third-party scripts execute. Also verify that you've published your banner after the most recent scan, so the runtime has an up-to-date script map.

Script still fires before consent (manual tagging)

Check that you have both required attributes: type="text/plain" and data-category="...". Missing either one means the browser will execute the script normally. Also make sure the CookieBeam runtime loads before the blocked scripts in your HTML.

External script file is still downloaded

Replace src with data-src. Even with type="text/plain", some browsers still fetch the file from the server if src is present. It just won't execute it.

Script doesn't activate after consent

Verify the data-category value matches one of the configured categories exactly (lowercase): necessary, analytics, marketing, or preferences. A typo or different casing will cause CookieBeam to ignore the script.

Multiple scripts depend on each other

CookieBeam loads external scripts sequentially in DOM order. If script B depends on script A, make sure script A appears first in your HTML. Inline scripts execute immediately after replacement, before the next external script starts loading.

Strict mode breaks a new script

When Strict Mode is enabled, any script not in the published script map is blocked by default. If your team adds a new script, it won't work until you run a scan, categorize it, and republish. Switch to Learning Mode temporarily if you need the script to work while you update the inventory.

Checklist Before Going Live

Script Blocking Checklist

  • Run a full site scan

    The scanner needs to discover all scripts and connections before the automatic blocking engine can do its job

  • Review and categorize all scripts in the dashboard

    Check the scanner inventory for any scripts classified as 'unknown' and assign them to the right category

  • Publish your banner with the updated script map

    The runtime only blocks scripts that are in the published script map

  • Manually tag inline scripts that need blocking

    Add type="text/plain" and data-category to any inline scripts hardcoded in your HTML

  • External scripts use data-src instead of src (manual tagging)

    Prevents the browser from fetching the file before consent

  • CookieBeam runtime loads first

    Place it before any other scripts in <head> so the interception engine installs its hooks early

  • Test with a fresh browser (no cookies)

    Clear all cookies and localStorage, then reload. No tracking requests should appear in DevTools Network tab.

  • Test consent flow end-to-end

    Accept cookies and verify blocked scripts activate. Check the Network tab for the expected requests.

  • Test reject flow

    Reject all cookies and verify no tracking requests are made on subsequent page loads

  • Consider enabling Strict Mode for production

    Strict Mode blocks unknown scripts by default. It's the safest option once your inventory is complete.

  • Verify Google Consent Mode still works

    If you use GTM, check that consent default and update commands still fire correctly alongside script blocking