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łówek | Co kontroluje | Kiedy go ustawiać |
|---|---|---|
| COOP | Czy inne okna/popupy mają dostęp do Twojego window | Zawsze — chroni przed tabnappingiem |
| COEP | Czy Twoja strona może ładować subresources bez deklaracji zgody | Tylko gdy potrzebujesz SharedArrayBuffer lub crossOriginIsolated |
| CORP | Kto 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 strony | Zalecana wartość |
|---|---|
| Strona marketingowa / blog | same-origin-allow-popups |
| SaaS z osadzeniami (YouTube, Stripe, Intercom) | same-origin-allow-popups |
| SPA z WebAssembly wielowątkowym | same-origin |
| Edytor wideo / gra w przeglądarce | same-origin |
| E-commerce z popupowym checkoutem | same-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 naapp.example.comiwww.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 timers —
performance.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:
- Skonfiguruj CDN, aby dodał
Cross-Origin-Resource-Policy: cross-origindo odpowiedzi (większość ma taką opcję). - Załaduj obrazki z
crossorigin="anonymous"i upewnij się, że CDN zwracaAccess-Control-Allow-Origin: *. - Użyj luźniejszego
COEP: credentiallesszamiastrequire-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
- CSP — ortogonalny. CSP kontroluje co ładujesz, COOP/COEP kontrolują warunki izolacji. Zobacz przewodnik po CSP dla usług Google.
- X-Frame-Options — dotyczy tego, czy Ty możesz być osadzony w iframe. Inna kategoria niż COOP.
- Referrer-Policy — niezwiązany.
- HSTS — niezwiązany. Zobacz kompletny przewodnik po nagłówkach HTTP.
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.



