Trzy nagłówki HTTP — Cross-Origin-Opener-Policy (COOP), Cross-Origin-Embedder-Policy (COEP) i Cross-Origin-Resource-Policy (CORP) — wspólnie budują mechanizm zwany cross-origin isolation. Poprawnie skonfigurowane chronią stronę przed atakami typu Spectre i tabnappingiem oraz odblokowują SharedArrayBuffer i high-resolution timery. Źle skonfigurowane po cichu łamią osadzenia YouTube, checkout Stripe i logowanie Google. Ten przewodnik pokazuje, jak ustawić je dobrze.

TL;DR — trzy nagłówki w 60 sekund

NagłówekCo kontrolujeKiedy go ustawiać
COOPCzy inne okna/popupy mają dostęp do Twojego windowZawsze — chroni przed tabnappingiem
COEPCzy Twoja strona może ładować subresources bez deklaracji zgodyTylko gdy potrzebujesz SharedArrayBuffer lub crossOriginIsolated
CORPKto może ładować Twoje zasoby (obrazy, skrypty)Na zasobach, które hostujesz dla innych witryn

Dla typowej witryny marketingowej / bloga wystarczy:

Cross-Origin-Opener-Policy: same-origin-allow-popups

Dla aplikacji webowej wymagającej SharedArrayBuffer (WebAssembly wielowątkowy, FFmpeg.wasm, gry, edytory wideo):

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Dlaczego te nagłówki powstały

Spectre (2018)

W styczniu 2018 opublikowano lukę Spectre — atak side-channel wykorzystujący speculative execution w procesorach. Umożliwił on odczytywanie pamięci z innych procesów, w tym innych zakładek przeglądarki. Dla przeglądarek to była katastrofa: JavaScript z reklamowej ramki mógł teoretycznie odczytać hasła z innej zakładki.

Odpowiedzią było Site Isolation (Chrome) oraz docelowo cross-origin isolation na poziomie HTTP. Strony, które chcą mieć dostęp do precyzyjnych timerów i współdzielonej pamięci (SharedArrayBuffer), muszą udowodnić, że są izolowane od potencjalnie wrogich źródeł.

Tabnapping i opener exploitation

Gdy otwierasz okno przez <a target="_blank"> lub window.open(), nowe okno ma dostęp do window.opener. Bez ochrony ta referencja pozwala złośliwej stronie podmienić zawartość okna rodzica (window.opener.location = 'phishing.com'). To klasyczny tabnapping.

rel="noopener" w linkach rozwiązuje problem dla pojedynczych linków, ale COOP rozwiązuje go na poziomie całej strony, niezależnie od tego, jak otwarto okno. Nagłówki cross-origin to jeden z wielu warstw obrony — przypadki takie jak atak na łańcuch dostaw wtyczek WordPress w 2026 pokazują, że bez strategii defense-in-depth pojedyncza luka potrafi otworzyć dostęp do setek tysięcy stron.

COOP — Cross-Origin-Opener-Policy

COOP decyduje, kto ma dostęp do Twojego obiektu window. Trzy dopuszczalne wartości:

unsafe-none (domyślna)

Cross-Origin-Opener-Policy: unsafe-none

Brak izolacji. Dowolna strona, która Cię otworzy przez window.open(), zachowuje referencję window.opener i może ją wykorzystać. Nie używaj na produkcji — nawet jeśli nie korzystasz z popupów, zostawiasz otwarte drzwi dla tabnappingu.

same-origin-allow-popups (zalecana dla większości stron)

Cross-Origin-Opener-Policy: same-origin-allow-popups
  • Izolacja przychodząca: strony innego originu otwierające Cię tracą dostęp do Twojego window.
  • Izolacja wychodząca: popupy, które Ty otwierasz, zachowują normalną komunikację z opener-em.

To złoty środek. Chronisz się przed tabnappingiem, ale nie łamiesz popupów OAuth, udostępniania, payments itp. Tego używamy na uper.pl.

same-origin (pełna izolacja)

Cross-Origin-Opener-Policy: same-origin

Pełna izolacja w obie strony. Żadna strona innego originu nie ma dostępu do Twojego window, ani Ty do ich. To konieczne do osiągnięcia crossOriginIsolated === true i odblokowania SharedArrayBuffer.

Cena: popupy z innych originów otwierane z Twojej strony natychmiast tracą window.opener. To właśnie łamie YouTube, Stripe i logowanie Google.

Tabela decyzyjna

Typ stronyZalecana wartość
Strona marketingowa / blogsame-origin-allow-popups
SaaS z osadzeniami (YouTube, Stripe, Intercom)same-origin-allow-popups
SPA z WebAssembly wielowątkowymsame-origin
Edytor wideo / gra w przeglądarcesame-origin
E-commerce z popupowym checkoutemsame-origin-allow-popups

COEP — Cross-Origin-Embedder-Policy

COEP wymusza, aby wszystkie ładowane przez Twoją stronę zasoby z innych originów jawnie zgodziły się być przez Ciebie osadzone. To druga połowa wymagania dla crossOriginIsolated.

unsafe-none (domyślna)

Brak wymagań. Ładujesz cokolwiek, skądkolwiek.

require-corp

Cross-Origin-Embedder-Policy: require-corp

Każdy zasób cross-origin (obraz, skrypt, iframe) musi albo:

  • Mieć nagłówek Cross-Origin-Resource-Policy: cross-origin (wyraźna zgoda), albo
  • Być załadowany z CORS (Access-Control-Allow-Origin).

Inaczej przeglądarka zablokuje zasób. To rygorystyczne — zepsuje większość integracji z CDN-ami i serwisami third-party, które nie ustawiają CORP/CORS.

credentialless (nowsze, 2022+)

Cross-Origin-Embedder-Policy: credentialless

Luźniejsza wersja: zamiast wymagać CORP/CORS, przeglądarka po prostu ładuje zasoby bez cookies i innych credentials. Dla większości zastosowań (obrazy, fonty, publiczne API) to wystarczy, a wymaga znacznie mniej zmian po stronie zewnętrznych dostawców.

Wsparcie przeglądarek: Chrome 96+, Firefox 119+. Safari dołączyło dopiero w 2024 — jeśli obsługujesz starsze iOS, miej fallback.

CORP — Cross-Origin-Resource-Policy

Odwrotna perspektywa. Jeśli Ty hostujesz zasoby (CDN, API obrazków, fonty dla innych witryn), CORP mówi przeglądarce kto może je ładować.

Wartości

Cross-Origin-Resource-Policy: same-origin      # tylko Twoja domena
Cross-Origin-Resource-Policy: same-site        # Twoja domena i subdomeny
Cross-Origin-Resource-Policy: cross-origin     # dowolna strona

Kiedy którą ustawiać

  • same-origin — zasoby prywatne (API zwracające dane użytkownika, obrazy z pulpitu).
  • same-site — zasoby współdzielone między subdomenami (logo na app.example.com i www.example.com).
  • cross-origin — publiczne zasoby, które chcesz, żeby inni osadzali (obrazki dla embedów, publiczne fonty, avatar CDN).

Jeśli jesteś dostawcą API i Twoi klienci narzekają, że ich strony z COEP przestały ładować Twoje obrazy — brakuje Ci Cross-Origin-Resource-Policy: cross-origin.

Magiczna flaga: self.crossOriginIsolated

Gdy jednocześnie ustawisz:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

w JavaScripcie dostajesz:

self.crossOriginIsolated === true

Co to odblokowuje:

  • SharedArrayBuffer — współdzielona pamięć między wątkami (Web Workers). Niezbędne dla wielowątkowego WebAssembly, FFmpeg.wasm, Three.js z Web Workers, emulatorów. Uwaga: pełna izolacja potrafi pogorszyć LCP, bo zasoby third-party wymagają dodatkowych nagłówków CORP/CORS i w razie ich braku są blokowane — co realnie spowalnia pierwsze renderowanie.
  • performance.measureUserAgentSpecificMemory() — precyzyjny pomiar zużycia pamięci.
  • High-resolution timersperformance.now() bez celowego rozmywania (mitigacja Spectre).
  • Atomics.wait() w głównym wątku.

Dla typowej witryny marketingowej to wszystko nie ma znaczenia. Dla aplikacji WebAssembly — kluczowe.

Realne problemy i rozwiązania

YouTube embed przestał działać

Objaw: wideo się renderuje, ale klikanie “Udostępnij”, logowanie, pełny ekran w niektórych przeglądarkach nie działa.

Przyczyna: masz ustawione Cross-Origin-Opener-Policy: same-origin. Player YouTube otwiera popupy, które natychmiast tracą referencję do swojego opener-a.

Rozwiązanie: zmień na same-origin-allow-popups. Jeśli potrzebujesz crossOriginIsolated (np. dla WASM), wyniesienie YouTube do osobnej strony bez izolacji.

Stripe Checkout popup nie zamyka się po płatności

Objaw: użytkownik płaci, popup pozostaje otwarty, Twoja strona nie wie, że transakcja się powiodła.

Przyczyna: Stripe po zakończeniu wywołuje window.opener.postMessage() do Twojej strony. Z COOP: same-origin referencja jest null.

Rozwiązanie: same-origin-allow-popups lub użycie Stripe Elements zamiast Checkout (renderuje się w Twoim iframe, nie popup).

Google Sign-In / OAuth callback nie dochodzi

Objaw: użytkownik loguje się w popupie Google, popup się zamyka, ale Twoja aplikacja nie dostaje tokena.

Przyczyna: identyczna jak Stripe — postMessage z popupa trafia do null-owego opener-a.

Rozwiązanie: same-origin-allow-popups. Alternatywnie — użyj Google Identity Services (GIS) z FedCM, który używa dedykowanego API zamiast popupów.

Obrazy z CDN się nie ładują po włączeniu COEP

Objaw: po dodaniu COEP: require-corp wszystkie obrazki z zewnętrznego CDN (Cloudinary, imgix, Gravatar) się nie ładują.

Przyczyna: CDN nie zwraca nagłówka Cross-Origin-Resource-Policy: cross-origin.

Rozwiązania:

  1. Skonfiguruj CDN, aby dodał Cross-Origin-Resource-Policy: cross-origin do odpowiedzi (większość ma taką opcję).
  2. Załaduj obrazki z crossorigin="anonymous" i upewnij się, że CDN zwraca Access-Control-Allow-Origin: *.
  3. Użyj luźniejszego COEP: credentialless zamiast require-corp.

Social sharing popup (Twitter/LinkedIn) po otwarciu nie odpowiada

Objaw: popup “Share on Twitter” otwiera się, ale interakcje w nim są dziwne / crashują.

Przyczyna: COOP: same-origin na Twojej stronie.

Rozwiązanie: same-origin-allow-popups.

Jak debugować

1. Sprawdź status izolacji

W konsoli DevTools:

self.crossOriginIsolated
// true  = masz pełną izolację
// false = nie masz

2. DevTools → Application → Frames

Chrome DevTools → zakładka Application → sekcja Frames → kliknij swoją stronę. Zobaczysz:

  • Cross-Origin Isolated: yes/no
  • Dokładne wartości COOP, COEP, CORP
  • Listę sub-frames i ich status izolacji

3. Reporty naruszeń

Możesz dostawać raporty naruszeń COOP:

Cross-Origin-Opener-Policy: same-origin; report-to="coop-endpoint"
Reporting-Endpoints: coop-endpoint="https://twojadomena.pl/coop-report"

Dostaniesz JSON z informacją, która ramka próbowała coś zrobić i została zablokowana. Kluczowe przy diagnozie cichych awarii.

4. Konsola przeglądarki

COEP blokady pojawiają się w konsoli jako:

A resource is blocked by OpaqueResponseBlocking, please check browser console for details.

Failed to load resource: net::ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep

Gotowe konfiguracje

Witryna marketingowa / blog (rekomendowane)

Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Resource-Policy: same-site

Bez COEP. Daje ochronę przed tabnappingiem, nie łamie żadnych integracji.

E-commerce z zewnętrznym checkoutem

Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Resource-Policy: same-site

Identyczna. Kluczowe — nie używać same-origin, bo zabije Stripe/PayPal popupy.

SPA z WebAssembly (SharedArrayBuffer)

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin

Plus: każdy zewnętrzny zasób (CDN, fonty, obrazki) musi mieć Cross-Origin-Resource-Policy: cross-origin lub CORS.

Aplikacja publicznego API / CDN

Cross-Origin-Resource-Policy: cross-origin

Tylko to. Mówisz światu: “możecie mnie używać, nawet z izolowanych stron”.

Interakcja z innymi nagłówkami

Najczęściej zadawane pytania o COOP, COEP i CORP

Co oznacza same-origin-allow-popups w Cross-Origin-Opener-Policy?

To wartość COOP, która izoluje Twoją stronę od okien przychodzących z innych originów (ochrona przed tabnappingiem), ale pozwala popupom, które Ty otwierasz, zachować komunikację z Twoją stroną. To najlepsza wartość dla większości witryn marketingowych i aplikacji używających integracji third-party (YouTube, Stripe, logowanie Google).

Dlaczego YouTube embed przestał działać po włączeniu COOP?

Prawdopodobnie ustawiłeś Cross-Origin-Opener-Policy: same-origin (pełna izolacja). Player YouTube otwiera popupy (Udostępnij, logowanie, pełny ekran), które przy pełnej izolacji tracą referencję do Twojej strony i komunikacja między nimi zawodzi. Zmień wartość na same-origin-allow-popups — zachowasz ochronę przed tabnappingiem, a YouTube znów będzie działał.

Jak włączyć SharedArrayBuffer w przeglądarce?

Musisz osiągnąć stan crossOriginIsolated === true. Wymaga to jednoczesnego ustawienia dwóch nagłówków HTTP: Cross-Origin-Opener-Policy: same-origin oraz Cross-Origin-Embedder-Policy: require-corp. Dodatkowo każdy zasób cross-origin, który ładujesz (obrazki, skrypty, fonty), musi mieć nagłówek Cross-Origin-Resource-Policy: cross-origin lub być załadowany z CORS.

Jaka jest różnica między COOP, COEP i CORP?

COOP (Cross-Origin-Opener-Policy) chroni Twoje window przed innymi stronami — dotyczy izolacji zakładek i popupów. COEP (Cross-Origin-Embedder-Policy) wymusza, aby zasoby, które Ty ładujesz z innych originów, jawnie wyraziły zgodę. CORP (Cross-Origin-Resource-Policy) to odwrotność COEP — wysyłasz go na zasobach, które Ty hostujesz, aby powiedzieć przeglądarce, kto może je ładować.

Czy zwykła strona z blogiem potrzebuje COEP i CORP?

Nie. Dla typowej strony marketingowej, bloga czy sklepu wystarczy Cross-Origin-Opener-Policy: same-origin-allow-popups. COEP i CORP są potrzebne dopiero, gdy Twoja aplikacja używa SharedArrayBuffer, wielowątkowego WebAssembly, albo hostujesz publiczne zasoby dla innych witryn.

Co zrobić, gdy CDN z obrazkami blokuje się po włączeniu COEP?

Trzy rozwiązania: (1) skonfiguruj CDN, aby dodawał Cross-Origin-Resource-Policy: cross-origin do odpowiedzi — większość CDN-ów ma taką opcję; (2) ładuj obrazki z atrybutem crossorigin="anonymous" i upewnij się, że CDN zwraca Access-Control-Allow-Origin: *; (3) użyj luźniejszej wartości Cross-Origin-Embedder-Policy: credentialless zamiast require-corp — nie wymaga zmian po stronie dostawców.

Czy ustawienie COOP łamie Google Analytics lub Google Tag Manager?

Nie. GA4 i GTM komunikują się przez fetch/XHR, nie przez popupy, więc żadna wartość COOP ich nie rozbija. Problemy pojawiają się dopiero przy interakcjach otwierających nowe okna: logowanie przez Google, Stripe Checkout, udostępnianie w social media. Zobacz też przewodnik po CSP dla usług Google.

Czym jest credentialless w Cross-Origin-Embedder-Policy?

To nowsza, luźniejsza wartość COEP (Chrome 96+, Firefox 119+, Safari 2024+). Zamiast wymagać, aby zewnętrzne zasoby wyraziły zgodę przez CORP lub CORS, przeglądarka po prostu ładuje je bez cookies i innych credentials. Dla publicznych zasobów (obrazki, fonty, publiczne API) to w zupełności wystarczy, a wymaga znacznie mniej zmian po stronie dostawców zewnętrznych.

Sprawdź cross-origin isolation z UPER SEO Auditor

Diagnozowanie cichych awarii COOP/COEP jest uciążliwe — objawy to dziwne zachowanie popupów i zepsuty OAuth, a nie czytelne komunikaty błędów. Zakładka Security w UPER SEO Auditor (wtyczka Chrome) pokazuje wszystkie trzy nagłówki dla aktualnej strony:

  • Cross-Origin-Opener-Policy — aktualna wartość i czy pasuje do rozsądnego defaulta
  • Cross-Origin-Embedder-Policy — wykryta lub brakująca, łącznie z obsługą credentialless
  • Cross-Origin-Resource-Policy — dla zasobów, które sama strona serwuje

Wszystkie trzy są wliczane do ważonego scoringu 0-10 razem z HSTS, CSP, X-Frame-Options, Referrer-Policy i Permissions-Policy. Zainstaluj wtyczkę, otwórz zakładkę Security i zobaczysz realne produkcyjne nagłówki bez sięgania do terminala.

Podsumowanie

Dla 95% witryn jedyny nagłówek z tej trójki, którego potrzebujesz, to:

Cross-Origin-Opener-Policy: same-origin-allow-popups

Chroni przed tabnappingiem, nie łamie niczego. COEP i CORP są potrzebne tylko, gdy hostujesz zasoby dla innych lub Twoja aplikacja używa SharedArrayBuffer.

Jeśli już masz wdrożone pełne izolowanie (same-origin + require-corp) i nagle zauważasz ciche awarie popupów — wróć do same-origin-allow-popups. Cena w bezpieczeństwie jest minimalna, cena w funkcjonalności gigantyczna.

Źródła