Wire / Cookie Banners / Article
┌── POST 09.04 · Cookie Banners · 7 min read

Blocking YouTube and Vimeo Embeds Until Consent: What the Libraries Actually Do

CookieConsent v3 blocks script tags and nothing else. Klaro handles iframes, but only via data-src. And the standard blocked-YouTube placeholder fetches its thumbnail from a Google host before consent — measured, with the response headers. How to wire embeds so the pre-consent page is genuinely quiet.

TL;DR

Script blocking and embed blocking are different problems, and most self-hosted consent setups only solve the first. CookieConsent v3 blocks script tags and nothing else — iframes need a separate plugin. Klaro handles iframes natively, but only if you move src to data-src first. And the standard “blocked” YouTube placeholder is not silent: measured on 2026-09-04, the documented iframemanager config fetches its thumbnail from i3.ytimg.com before anyone consents to anything. What each library does, what we measured, and how to wire it so the pre-consent page is quiet.

A consent library that blocks scripts correctly can still leak on the first paint, because the thing leaking is not a script. It is an <iframe> holding a YouTube video, a Google Map or a Vimeo player — markup a content editor pasted into a page two years ago, which no consent configuration ever looked at.

An iframe with a live src starts loading while the HTML is still being parsed. It does not wait for your banner, it does not care about your categories, and by the time your consent library initialises the request has already left.

What “script blocking” actually covers

The blocking mechanism these libraries document is a script-tag rewrite. In CookieConsent v3 you set type="text/plain" and data-category="analytics", and the library restores the real type once the category is accepted.

In the v3.1.0 source the selector is defined once, as data-category, and used to query script[data-category]. Script elements. Nothing else in the source touches an iframe, and the project’s FAQ says so plainly: to block iframes, use iframemanager, a separate 313-star MIT plugin by the same author, last released as v1.3.0.

So if you followed our CookieConsent and Consent Mode v2 guide and stopped there, every embed on your site is currently unblocked. That is the boundary of what the library claims to do, not a bug in it.

What each library does with an iframe

Klaro: native, with one required change to your markup

Klaro (BSD-3-Clause, 1,506 stars, version 0.7.22, last commit 27 March 2025) is the only one of the three that handles iframes in core. Its consent manager branches on element.tagName === 'IFRAME', holds the URL in data-src, and keeps src empty and the element hidden until consent arrives. This only works if src was never live: Klaro also blanks a live src when it initialises, which is too late, and we measured what “too late” means below.

CookieConsent v3: a second plugin, wired by hand

iframemanager runs alongside CookieConsent rather than inside it, and the two have to be connected in both directions: onAccept/onReject per service calling im.acceptService(), and iframemanager’s onChange calling back into CookieConsent.acceptService(). The project documents this pairing; it is not automatic.

tarteaucitron: service wrappers, local placeholder

tarteaucitron (MIT, 1.34.0) takes the third approach: you replace the embed markup with its own <div>, and the service definition builds the iframe after consent. Its refusal-state placeholder is generated locally — text and a button — with no remote asset of any kind. Its Consent Mode wiring is in our tarteaucitron setup guide.

What we measured

Three pages, served locally, loaded in Chromium with the network log captured before touching anything.

Setup Third-party requests before consent
Klaro, iframe with data-src None. Two requests, both first-party.
Klaro, iframe with a live src www.youtube-nocookie.com/embed/…, then net::ERR_ABORTED
iframemanager, documented YouTube config i3.ytimg.com/vi/…/hqdefault.jpg200

The middle row is the interesting failure. The request is genuinely initiated — the parser reaches the iframe long before Klaro runs — and is then aborted when Klaro blanks the src. Whether Google’s edge had already accepted the connection is a race on network timing your page does not control, and the abort makes the network panel look clean while telling you nothing about what reached the other end. Use data-src.

The placeholder is not silent either

The third row is the one worth sitting with, because it is the recommended configuration. iframemanager’s documented YouTube service points thumbnailUrl at https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg, so the poster frame behind the “Load video” button is fetched from a Google-owned host on page load, with no consent involved. The documented Vimeo and Dailymotion services do the same through their providers’ APIs. Here is what came back, in full:

GET https://i3.ytimg.com/vi/<video-id>/hqdefault.jpg  200

content-type:   image/jpeg
etag:           "1749462010"
cache-control:  public, max-age=7200
server:         sffe
(no Set-Cookie)

No cookie is set. That is worth saying clearly, because it is the first thing people assume. But the request still carried the visitor’s IP address, User-Agent and Referer to Google, and the response is an ETag-tagged resource cached in the browser for two hours.

Whether that is inside Article 5(3) of the ePrivacy Directive is not something a blog post decides. The primary source is the EDPB’s Guidelines 2/2023 on the technical scope of Article 5(3), version 2.0, adopted 7 October 2024, where three paragraphs bear on it directly. At paragraph 50, distribution of a resource to the user’s terminal equipment “does constitute storage, at the very least through the caching mechanism of the client-side software. As such, Article 5(3) ePD is applicable, even if this storage is not permanent.” At paragraph 51, adding tracking information to a URL or image “constitutes an instruction to the terminal equipment to send back the targeted information”, which the Board treats as gaining access. At paragraph 55, access to an IP address engages Article 5(3) where the address originates from the user’s terminal equipment.

And immediately after, at paragraph 56, the Board reminds everyone that applicability “does not systematically mean that consent needs to be collected” — exemptions have to be assessed case by case. That caveat is part of the source and belongs in any honest summary of it. The engineering point survives the legal uncertainty either way: if your position is “nothing third-party is contacted before consent”, the default configuration makes that statement false.

While we are here: youtube-nocookie

All three libraries embed via www.youtube-nocookie.com, where the domain name does more persuading than the documentation. Google’s support page for privacy-enhanced mode describes limits on use — a view “will not be used to personalize the YouTube browsing experience”, nor “to personalize advertising shown to the viewer outside of your site or app”. It does not say that nothing is stored. Read it as a reduction in downstream use, not as an absence of storage.

Wiring it so the pre-consent page is quiet

Klaro

<script>
var klaroConfig = {
  services: [{
    name: 'youtube',
    title: 'YouTube',
    purposes: ['media'],
    contextualConsentOnly: true
  }]
};
</script>

<iframe width="560" height="315"
        data-name="youtube"
        data-src="https://www.youtube-nocookie.com/embed/VIDEO_ID"></iframe>

Klaro inserts a placeholder before the iframe and renders its contextual notice into it. Two behaviours are worth knowing before you rely on them, both from the source rather than the docs.

contextualConsentOnly does less than the name suggests. It appears in exactly one place in the codebase: a filter in changeAll(), so “accept all” and “decline all” skip the service. It does not remove the service from the preferences modal — it still renders under its purpose, which we confirmed in the browser.

Supplying your own placeholder disables Klaro’s notice. Klaro only renders its notice when the preceding element is not already a matching data-type="placeholder". Add your own and you own the accept button too — and the obvious call does not work:

const manager = klaro.getManager();
manager.updateConsent('youtube', true);

manager.applyConsents();                          // no-op before the
                                                  // main banner is confirmed
manager.applyConsents(false, true, 'youtube');    // this one loads it

The signature is applyConsents(dryRun, interactive, serviceName), and the service is only treated as consented when one of confirmed, optOut, dryRun or interactive is set. Call it with no arguments from a custom placeholder and nothing happens, silently, with no error.

iframemanager

Keep the plugin and remove the remote thumbnail. A per-element data-thumbnail takes precedence over the service-level thumbnailUrl, and an empty value suppresses the background image entirely:

// serve your own poster frames
thumbnailUrl: '/media/posters/{data-id}.jpg'
<!-- or no poster at all, per embed -->
<div data-service="youtube" data-id="VIDEO_ID" data-thumbnail=""></div>

Downloading the poster once at build time removes a third-party request from every page view.

How to check your own site

Load a page with an embed in a clean profile, do not touch the banner, and filter the network panel for the provider’s hosts — ytimg.com, youtube.com, youtube-nocookie.com, vimeo.com, maps.googleapis.com, gstatic.com. Anything with a status code is a request that happened. Then check more than one page: embeds live in body content, so the offending markup is usually on a blog post or a location page, never on the homepage you tested. The Playwright script we published for pre-consent detection takes a URL list and does this across all of them.

An embed loaded after consent still leaves what it wrote behind if the visitor later withdraws — a separate failure mode.

Embeds are the part of a site that consent configuration never sees, because nobody wrote them into the config. Scan your pages to see which third-party hosts load before consent with CookieInspector.

C
About the author
Consent Mode HQ
Editorial team at Consent Mode HQ
Read more by author ↗