Some browser APIs are intentionally hard to get: they are powerful enough to amplify timing attacks if any hostile data shares a process with your page. Browsers therefore reserve features such as SharedArrayBuffer, APIs like measuring user-agent-specific memory, and timers with extra precision for documents that opt into a stricter boundary. That boundary is what we call cross-origin isolation.
The web’s baseline rule is the same-origin policy: a page at one origin cannot freely read private data from another. In practice the platform still allows many cross-origin affordances that grew up over decades—embedding iframes, pulling in scripts and images, opening windows that keep a live reference to each other. Servers and browsers layered mitigations on top, notably Cross-Origin Resource Sharing (CORS) for explicit permission to read cross-origin responses, and “opaque” representations where scripts can use a resource in limited ways without seeing raw bytes—think of drawing a cross-origin image to a canvas without CORS.
Those rules are reasoned about in units called browsing context groups: sets of documents that can reach each other in ways that matter for security. For a long time, blocking direct reads plus CORS was enough for many threat models.
Side-channel attacks such as Spectre changed the calculus. Code that shares a browsing context group with sensitive data can sometimes infer memory contents by measuring how long certain operations take. Coarser timers make the attack slower; facilities that expose shared memory or very precise timing make it far more practical. That is why a cross-origin image embedded without real isolation is no longer “opaque enough” if it still ends up in the same microarchitecturally dangerous neighborhood as attacker code.
The mitigation strategy is strict vetting: do not let data into your page’s browsing context group unless the resource owner agreed. Cross-origin isolation is the state where the platform can credibly promise that rule. Enabling it is primarily a pair of HTTP headers: Cross-Origin-Embedder-Policy (COEP) and Cross-Origin-Opener-Policy (COOP). Together they keep your top-level document from casually mixing with untrusted cross-origin surfaces the way older defaults allowed.
COEP with the value require-corp declares that subresources must either come from the same origin or explicitly opt in. Cross-origin images, workers, and similar loads must be fetchable under CORS or marked with an appropriate Cross-Origin-Resource-Policy (CORP) value so owners can say who may embed them. The crossorigin attribute on elements such as img matters here: it switches the request into CORS mode so a cooperating server can answer with the right headers.
COOP addresses the window relationship side. A top-level document that sends Cross-Origin-Opener-Policy: same-origin is isolated from unrelated documents: for example, a popup’s window.opener can be null, and references from the opener behave as if the relationship were severed. Variants like same-origin-allow-popups trade off strictness where you still need vetted popups; unsafe-none is the legacy-open default. Separately, rel="noopener" on links you open still helps on the opener side in older stacks, but it is not a substitute for declaring COOP on your own document.
If you need the gated APIs, you are aiming for both headers in the right combination—typically COEP: require-corp plus COOP: same-origin—so the runtime can treat your page as cross-origin isolated. You can verify success in JavaScript with self.crossOriginIsolated. Implementation details, CDN quirks, and third-party widgets are the fiddly part; treat this piece as the “why” and follow a dedicated deployment guide for the exact nginx, CDN, or framework steps.