youtube-nocookie GDPR: proč to nestačí (a co dělat místo toho)

Vkládáte na web YouTube video? Pak řešíte GDPR, i když jste o tom možná nikdy nepřemýšleli. A pokud jste to už řešili tak, že jste přepnuli embed na doménu youtube-nocookie.com, mám pro vás nepříjemnou zprávu: není to řešení zcela v souladu s GDPR. Je to krok správným směrem. Není to hotovo.

Před pár měsíci jsem psal o rozsudku, který v Hannoveru rozhodl, že Google Tag Manager bez souhlasu porušuje GDPR — a to jen proto, že GTM sám o sobě umí načíst kód třetí strany. YouTube embed je stejný případ, jen v jiném hávu: cizí doména, cizí skript, cizí server, který dostane IP adresu vašeho návštěvníka dřív, než na cokoliv klikne. Tenhle článek na to navazuje — jen místo GTM je hlavní postavou YouTube.

youtube-nocookie GDPR: co Google slibuje a co zamlčuje

Zdánlivé řešení má oficiální jméno — Google tomu říká Privacy Enhanced Mode. Slibuje dvě konkrétní věci:

  1. zhlédnutí videa přes nocookie doménu neovlivní personalizaci obsahu na YouTube.
  2. případné reklamy k videu nebudou personalizované.

To je celý oficiální slib.

Všimněte si, co v něm chybí. Google neříká, že se o zobrazení videa nic neukládá. Neříká, že se nepřenáší žádná data. Slibuje jen nepersonalizaci — ne anonymitu.

A realita je přesně mezi tím:

  • Při samotném načtení stránky s embedem (i tím na nocookie doméně) odchází IP adresa vašeho návštěvníka na servery Googlu. To je fakt, ne interpretace — jde o přenos osobních údajů.
  • youtube-nocookie.com zapisuje do prohlížeče hned při načtení stránky, ještě než na cokoliv kliknete. Otestoval jsem to: okamžitě po načtení embedu přibyly v localStorage záznamy yt-icons-last-purged a ytidb::LAST_RESULT_ENTRY_KEY. Doména se sice jmenuje nocookie, zápisům do prohlížeče se ale nevyhnete ani bez jediného kliknutí. Snadno si to ověříte sami: otevřete DevTools, záložku Application → Local Storage.
  • Jakmile návštěvník klikne na Play, nastaví se plnohodnotné YouTube cookies. Od tohohle okamžiku se chování prakticky neliší od běžného embedu.

Jednu věc bych rád preventivně vyvrátil: nejde o to, že by Google nezákonně posílal data do USA. Google je od roku 2023 certifikovaný v programu EU-US Data Privacy Framework, takže tenhle konkrétní strašák neplatí. Jádro problému je jinde — embed YouTube videa není nezbytný pro provoz webu, takže podle ePrivacy směrnice a GDPR vyžaduje aktivní souhlas. V Česku to platí v opt-in režimu od novely zákona o elektronických komunikacích, účinné od ledna 2022.

Takže: youtube-nocookie je legitimní krok. Omezuje personalizaci a na rozdíl od běžného embedu, který ukládá YouTube cookies hned při načtení stránky, je nastaví až ve chvíli, kdy návštěvník klikne na Play. Sám o sobě ale souhlas neřeší.

Nejhorší varianta: přepis domény JavaScriptem po načtení stránky

Když jsem hledal, jak lidé s tímhle problémem u nás zápasí, narazil jsem na návody (a to i od oficiálního poskytovatele cookie lišty), které doporučují elegantní trik: necháte web vykreslit běžný YouTube embed a pak JavaScriptem po načtení stránky přepíšete atribut src z youtube.com na youtube-nocookie.com. Vypadá to chytře. Technicky to ale nedává smysl.

Prohlížeč začíná stahovat obsah iframe ve chvíli, kdy narazí na jeho src atribut v HTML — tedy dřív, než se stihne spustit váš JavaScript, který ho má přepsat. Vzniká souboj o milisekundy: request na youtube.com může odejít dřív, než ho skript stačí opravit. Jak rychle k tomu dojde, závisí na tom, kde ve stránce se skript nachází, jak je stránka vykreslovaná a kolik dalšího kódu běží před ním. Na ochranu osobních údajů takový mechanismus rozhodně není spolehlivý.

Navíc — stále jde o to, že váš web dělá request na cizí doménu a přenáší tam data. GDPR to nijak neřeší, jen to oblafne lidi, co tomu nerozumí.

Tohle je přesně ten typ detailu, který v auditu cookie lišty a vloženého obsahu vidím pravidelně — netýká se to jen YouTube, ale všeho, co web natahuje odjinud, podobně jako reklamní pixely automaticky čtou formuláře na webu. Pokud si nejste jistí, co váš web posílá dál, dá se to ověřit v auditu.

Věděli jste? — informační panel

Vědecká poznámka

Dokud návštěvník neklikne na náhled videa, je stránka zároveň ve dvou stavech: „video je tu“ i „video tu není“. Rozhodne to až samotný klik — trochu jako Schrödingerova kočka, jen místo krabice máte iframe a místo jedu souhlas. A přesně na tomhle principu funguje řešení, ke kterému se teď dostáváme.

Skutečné řešení: dvě vrstvy, ne jedna

Pro skutečně čisté řešení je potřeba udělat dvě věci: změnit doménu embedu už na serveru a načítat samotné video až ve chvíli, kdy o něj návštěvník sám požádá. Pojďme na ně popořadě.

Vrstva 1 — změna domény na serveru, ne v prohlížeči

Pokud embed opravdu potřebujete natáhnout hned (typicky proto, že souhlas už byl dřív udělen a video se má přehrát rovnou), doménu přepište tam, kde vzniká HTML — na serveru, v šabloně nebo přes filtr obsahu vašeho redakčního systému. Na WordPressu to typicky znamená zásah do filtru, který upravuje obsah při vykreslování stránky (např. the_content), nebo úpravu šablony, která embed generuje — ne skript běžící až v prohlížeči. Na jiných systémech hledejte stejné místo — tam, kde se HTML sestavuje na serveru, ne tam, kde ho prohlížeč jen zobrazuje. Prohlížeč pak nikdy nedostane šanci kontaktovat youtube.com, protože v HTML, které mu přijde, je od začátku jen youtube-nocookie.com. Žádný souboj o milisekundy, žádné spoléhání na to, jestli se skript stihne spustit včas.

Do zadání pro vývojáře nebo agenturu patří přesně tahle věta: doména se mění na backendu při generování stránky, ne JavaScriptem po jejím načtení.

Vrstva 2 — video se načte až po kliknutí na náhled (facade pattern)

Facade pattern je v EU běžně doporučovaný postup — dvouklikové řešení (two-click) má dlouhou tradici hlavně v Německu — a princip je jednoduchý: místo iframe se na stránce zobrazí statický náhledový obrázek s tlačítkem Play. Skutečný YouTube iframe (na nocookie doméně) se natáhne, teprve když návštěvník na náhled klikne — přesně ten moment ze Schrödingerovy kočky, kdy se rozhodne, jestli video „je“ nebo „není“.

Náhled YouTube videa

Kliknutím načtete video z YouTube, což může zahrnovat přenos dat společnosti Google. Více informací

Ještě jeden detail — i samotný náhledový obrázek z img.youtube.com posílá IP adresu Googlu, protože ho prohlížeč natahuje z jeho serverů. Plně čisté řešení proto hostuje náhledy lokálně — stáhnete si je jednou při publikaci obsahu a servírujete je z vlastní domény. Teprve klik na Play spouští cokoliv, co komunikuje s Googlem.

Implementaci si nemusíte vymýšlet od nuly — existují hotová řešení tohoto principu (např. open-source komponenta lite-youtube-embed nebo pluginy pro WordPress, které facade pattern implementují).

Pokud chcete vidět princip přímo v kódu, tady je minimální funkční příklad bez závislostí — HTML náhled, CSS pro tlačítko Play a JavaScript, který teprve po kliknutí vytvoří skutečný iframe:

<div class="yt-facade" data-video-id="KOD_VIDEA" role="button" tabindex="0" aria-label="Přehrát video z YouTube (načte se až po kliknutí)">
  <img src="/MUJ_OBRAZEK.jpg" alt="Náhled YouTube videa" width="720" height="405" loading="lazy">
  <span class="yt-facade__play" aria-hidden="true"></span>
    <p class="yt-facade__note">Kliknutím načtete video z YouTube, což může zahrnovat přenos dat společnosti Google. <a href="/privacy-policy/">Více informací</a></p>
</div>

<style>
.yt-facade { position: relative; display: block; cursor: pointer; width: 100%; max-width: 720px; }
.yt-facade img { display: block; width: 100%; height: auto; }
.yt-facade__play {
  position: absolute; inset: 0; margin: auto; width: 68px; height: 48px;
  background: rgba(0, 0, 0, .75);
  border-radius: 10px;
}
.yt-facade__play::before {
  content: "";
  position: absolute; top: 50%; left: 54%; transform: translate(-50%, -50%);
  border-style: solid; border-width: 12px 0 12px 20px;
  border-color: transparent transparent transparent #fff;
}
.yt-facade__note {
  position: absolute; right: 8px; bottom: 8px; margin: 0;
  max-width: calc(100% - 16px);
  font-size: .75em; line-height: 1.35; text-align: right;
  color: #fff; background: rgba(0, 0, 0, .6);
  padding: .35em .6em; border-radius: 6px;
}
.yt-facade__note a { color: #fff; text-decoration: underline; }
.yt-facade__iframe {
  display: block; width: 100%; max-width: 720px; aspect-ratio: 16 / 9;
border: 0;
}
</style>

<script>
(function () {
document.querySelectorAll('.yt-facade:not([data-bound])').forEach(function
(facade) {
    facade.setAttribute('data-bound', '1');

    var play = functio
      var videoId = facade.dataset.videoId;

      var iframe = doc;
      iframe.className = 'yt-facade__iframe';
      iframe.src = 'https://www.youtube-nocookie.com/embed/' + videoId +
'?autoplay=1';
      iframe.title = 'Přehrávač YouTube videa';
      iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
      iframe.allowFullscreen = true;

      facade.replaceWith(iframe);
    };

    facade.addEventList) {
      if (event.target.closest('a')) return; // klik na odkaz nespouští video
      play();
    });
    facade.addEventListener('keydown', function (event) {
      if (event.target.closest('a')) return; // Enter na odkazu otevírá odkaz, ne video
      if (event.key === 'Enter' || event.key === ' ') {
        event.preventD
        play();
      }
    });
  });
})();
</script>

Tři věci v tomhle kódu dělají rozdíl mezi „vypadá to čistě“ a „je to čisté“:

  1. Do HTML stránky nejde žádný iframe ani skript, který by ho natahoval při načtení — jen div s obrázkem. Prohlížeč se serverů YouTube nedotkne, dokud se nespustí funkce play(). Nově vytvořený iframe navíc dostává vlastní CSS třídu (yt-facade__iframe), takže zůstává stejně responzivní jako náhled, který nahradil — ne pevně velký prvek zafixovaný na rozměr z okamžiku kliknutí.
  2. facade.replaceWith(iframe) běží výhradně z kliknutí nebo klávesové akceclick a keydown (Enter nebo mezerník). Parametr autoplay=1 v URL pak funguje bez problémů, protože ho prohlížeč vyhodnotí jako reakci na akci uživatele, ne jako automatické spuštění — pomáhá tomu i atribut iframe.allow s hodnotou autoplay o pár řádků výš.
  3. role="button" a tabindex="0" dělají z náhledu ovladatelný prvek i pro lidi, co web ovládají klávesnicí nebo čtečkou obrazovky — bez toho by facade pro ně byl jen neklikatelný obrázek. noscript fallback zase řeší situaci, kdy má návštěvník JavaScript vypnutý — dostane aspoň přímý odkaz na YouTube, ne mrtvý náhled.

A ten lokálně hostovaný náhled zmíněný výše? Stáhnete si ho jednou (např. z img.youtube.com/vi/ID_VASEHO_VIDEA/maxresdefault.jpg v momentě, kdy obsah publikujete) a uložíte na vlastní server nebo CDN. Pozor — maxresdefault.jpg existuje jen u části videí (typicky ne u starších nebo u videí mimo poměr 16:9), takže si před nasazením ověřte, že se soubor opravdu stáhl. Jako bezpečnější výchozí volbu použijte hqdefault.jpg, který je dostupný prakticky vždy. Žádný živý request na Google při načtení stránky. Tlačítko Play navíc není samostatný obrázek, ale .yt-facade__play vykreslený čistě přes CSS — takže neřešíte druhou sadu assetů pro hover nebo focus stav, jen barvu a stín.

Poznámka na okraj: videoId jde v kódu přímo do URL iframe. Pokud pochází z vlastního pole v CMS, které upravuje jen redaktor s právy, je to v pořádku. Pokud by mohlo přijít z libovolného uživatelského vstupu, validujte formát (11 znaků, písmena, čísla, pomlčka, podtržítko), než se dostane do adresy.

Pokud si kód nechcete psát ručně, dělá přesně tohle i zmíněná komponenta lite-youtube-embed nebo odpovídající WordPress plugin — princip zůstává stejný, jen ho za vás implementuje hotová knihovna.

U náhledu se navíc vyplatí napsat jednu větu o tom, co se po kliknutí stane — třeba „Kliknutím načtete video z YouTube, což může zahrnovat přenos dat společnosti Google.“ Není to právničina, je to informace pro člověka.

Implementace z praxe

Realita bývá míň černobílá, než toto řešení naznačuje. U jednoho klienta jsme narazili na web, kde popisky produktů — včetně videí — tahal JavaScript z externí služby třetí strany, do jejíž integrace jsme neměli přístup. Předělat celý tenhle feed na 100 % čisté řešení s lokálními náhledy nebylo technicky reálné. Zůstalo se aspoň u přepisu domény na nocookie jako pragmatického zlepšení.

Není to dokonalé. Ale je to lepší než nic, a hlavně jsme přesně věděli, co za riziko klientovi zbývá — a napsali jsme to do podmínek webu. Svět není černobílý. I částečné zlepšení má cenu, pokud víte, co vám po něm ještě zbývá vyřešit.

Řekněte to v podmínkách lidsky, ne právničinou

Ať zvolíte kteroukoliv kombinaci obou vrstev, patří to do cookie lišty a zásad ochrany osobních údajů — srozumitelnou větou, ne odstavcem právničiny. Něco jako: „Videa z YouTube se na této stránce načítají až po vašem souhlasu. Po kliknutí na náhled dojde k přenosu dat společnosti Google, včetně IP adresy.“

Tahle část se často odbude, protože nejde o metriku, kterou by někdo hlídal v dashboardu. Ve stejné kategorii chyb jsem popisoval 5 nejčastějších chyb v nastavení cookie lišty — z auditů mi vychází, že zhruba třetina firem má cookie lištu nastavenou špatně. Vložený YouTube je jen další komponenta, kde se stejná chyba opakuje, jen si toho nikdo nevšimne, dokud se na to nikdo nepodívá zblízka.

Shrnutí

Sám o sobě youtube-nocookie GDPR neřeší — omezí personalizaci přehrávání a reklam, ale neblokuje přenos IP adresy při načtení ani zápis identifikátorů do prohlížeče. Skutečné řešení stojí na dvou věcech: doménu měňte na backendu, nikdy JavaScriptem po načtení stránky, a video načítejte až ve chvíli, kdy na náhled někdo klikne (facade pattern).

Pokud si po přečtení nejste jistí, jestli váš web dělá aspoň tu první vrstvu pořádně, stojí za to se na to podívat společně — v jednom auditu se obvykle najde víc než jen YouTube. Domluvme si konzultaci.