# How to block third-party scripts until consent

> A developer's guide to blocking third-party scripts until consent: inert script tags, activation code, tag managers, embeds and how to test that nothing leaks.

Source: https://cookiesh.eu/blog/block-scripts-until-consent  
Published: 2026-08-20

## Key takeaways

- Non-essential scripts must not run, set cookies or read device data until the visitor opts in.
- A script with type text/plain is ignored by the browser, so it can be activated later without a page reload.
- Scripts inserted with JavaScript run asynchronously by default; set async to false when external scripts must run in order.
- Inline snippets, iframes, pixels, npm SDKs and tag manager tags all need gating, not just script tags with a src.
- Verify blocking in a fresh browser profile by checking network requests and storage before clicking anything.

To block third-party scripts until consent, ship them in a form the browser will not execute, such as a `<script>` tag with `type="text/plain"`, and activate them only after the visitor opts into the matching category. Apply the same rule to inline snippets, iframes, tracking pixels and tag manager tags, then verify it in a clean browser before clicking anything.

## Why do third-party scripts need to wait for consent?

Because most of them store or read information on the visitor's device the moment they run, and in the EU that requires prior consent. Article 5(3) of the ePrivacy Directive requires consent for storing or accessing information on a device, unless it is strictly necessary for a service the user explicitly asked for.

Analytics, advertising, social and many embed scripts set cookies, read browser storage or collect device characteristics as soon as they execute. In Planet49 (C-673/17), the Court of Justice of the EU confirmed that the rule applies whether or not the information is personal data, and that pre-ticked boxes are not valid consent. A banner that appears while trackers are already running does not meet that standard.

Scripts that are genuinely needed for the service, such as payment, fraud prevention or the consent tool itself, can load normally; see [what counts as a strictly necessary cookie](/blog/strictly-necessary-cookies).

## How do I block scripts before consent?

Change their `type`. Browsers only download and execute scripts whose `type` is a JavaScript type or `module`; a script marked `text/plain` is treated as inert data. Keep the original attributes and add a category so your consent code knows what to activate.

```html
<!-- Inert: the browser neither downloads nor runs these -->
<script type="text/plain" data-consent="analytics"
        src="https://analytics.example.com/script.js"></script>

<script type="text/plain" data-consent="marketing">
  window.adPixel = window.adPixel || [];
  window.adPixel.push(['init', 'PIXEL_ID']);
</script>

<!-- Embeds: keep the real URL in data-src until consent -->
<iframe data-consent="marketing"
        data-src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
        title="Product demo" width="560" height="315"></iframe>
```

Remove the `<noscript>` image fallbacks that many vendor snippets include. They fire a request when JavaScript is disabled, which is exactly when your banner cannot appear, so there is no way to gate them.

## How do I activate blocked scripts after consent?

Replace each inert script with a fresh `<script>` element. Changing the `type` attribute on an existing element does not make the browser run it, and scripts added through `innerHTML` never execute, so you need a new element created with `document.createElement`.

```js
function activate(category) {
  document
    .querySelectorAll(`script[type="text/plain"][data-consent="${category}"]`)
    .forEach((inert) => {
      const live = document.createElement('script');
      for (const { name, value } of inert.attributes) {
        if (name !== 'type') live.setAttribute(name, value);
      }
      if (!inert.hasAttribute('async')) live.async = false; // keep insertion order
      live.text = inert.text; // inline code, if any
      inert.replaceWith(live);
    });

  document
    .querySelectorAll(`iframe[data-consent="${category}"][data-src]`)
    .forEach((frame) => { frame.src = frame.dataset.src; });
}

// Apply the stored choice on load; call activate() again from your banner's handlers.
for (const [category, granted] of Object.entries(readStoredConsent())) {
  if (granted) activate(category);
}
```

Three details trip people up:

- **Order.** External scripts inserted with JavaScript run asynchronously by default, in whatever order they finish downloading. Setting `async = false` makes them run in insertion order, which matters when one library depends on another.
- **Inline snippets run immediately.** An inline script executes as soon as it is inserted, without waiting for external scripts. Most vendor snippets queue their calls until the library loads, but check any custom code that assumes a library is already there.
- **`document.write()`.** Old snippets that call it will not work once the page has loaded. Replace them with the vendor's asynchronous snippet.

## Which blocking method should I use?

Most sites combine several methods, chosen by where each tag lives.

| Method | Best for | Watch out for |
| --- | --- | --- |
| Inert `text/plain` tags | Tags hard-coded in templates | Every tag must be edited; execution order |
| Consent-aware loader in your own code | SPAs and SDKs installed from npm | SDKs that initialize as soon as they are imported |
| Tag manager consent settings | Teams that manage tags in a tag manager | Each tag needs the correct consent requirement |
| Automatic blocking | Large legacy sites with many hard-coded tags | Must load synchronously first; can miss or break scripts |
| Iframe placeholders | Video, maps and social embeds | The placeholder must explain what loading it means |

Libraries installed from npm never appear as script tags, so the gate is your own code. Call the SDK's `init()` only after consent, and load it with dynamic `import()` so it is not even downloaded before. That pattern, and handling choices that change mid-session, is covered in [handling consent in single-page apps](/blog/consent-in-single-page-apps).

Google tags have an extra option. With Google Consent Mode, tags can load before consent and adjust their behavior to the consent signals instead of being blocked. Whether that setup fits your risk appetite is a separate decision; see [basic vs. advanced consent mode](/blog/basic-vs-advanced-consent-mode).

## How do I verify that blocking works?

Test like a first-time visitor, and check what actually happens on the network and in storage:

1. Open the site in a fresh private window from a region where your banner appears.
2. Before clicking anything, open DevTools and check the Network panel. There should be no requests to analytics or advertising domains, unless you deliberately chose a setup that sends them.
3. In the Application panel, check cookies, local storage and session storage. Only strictly necessary entries should exist.
4. Click "Reject all" and confirm that nothing new loads or gets stored.
5. In a new session, accept one category at a time and confirm that only its scripts and embeds load.
6. Repeat after every release that adds a tag, and run a periodic [cookie audit](/blog/how-to-run-a-cookie-audit) to catch tags added outside your release process.

## Bottom line

Blocking is about execution, not just cookies: once a non-essential script runs, it can store or read data before anyone has agreed. Ship tags inert, activate them per category after an opt-in, gate embeds and SDKs the same way, and test in a clean browser. Tools like Cookiesh can automate the activation step, running scripts tagged `type="text/plain" data-cookiesh="analytics"` only after the visitor opts into that category.

*This article is general information, not legal advice. For decisions about your specific situation, consult a qualified privacy professional.*

## FAQ

### Is it enough to block cookies but still load the script?

Usually not. The EU rule covers storing and accessing any information on the device, not only cookies, and a running script can use local storage, read device characteristics or send identifiers. Not executing the script at all is the reliable approach.

### Can I load Google Tag Manager before consent?

Many sites do, and use the container's consent settings so individual tags fire only when the relevant consent is granted. Check what the container and every tag actually do before consent in a fresh browser profile rather than assuming the configuration is right.

### Do I need to block scripts for visitors outside the EU?

It depends on the law that applies to them. Opt-in rules such as the EU's and the UK's require blocking before consent. Under California's CCPA and CPRA, the duty is mainly opt-out: you must stop selling or sharing personal information when a visitor opts out, including through a Global Privacy Control signal.

## Sources

- [Directive 2002/58/EC (ePrivacy Directive), EUR-Lex](https://eur-lex.europa.eu/eli/dir/2002/58/oj)
- [CJEU case C-673/17 (Planet49)](https://curia.europa.eu/juris/liste.jsf?num=C-673/17)
- [EDPB Guidelines 05/2020 on consent](https://www.edpb.europa.eu/our-work-tools/our-documents/guidelines/guidelines-052020-consent-under-regulation-2016679_en)
- [MDN: Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
