What do the response headers Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp each do to a document, and why must a page send both before window.crossOriginIsolated is true?
answer
- two directions data can arrive
- openers and popups on one side
- embedded subresources on the other
- consent proves nobody unconsenting shares
- capability gate is a conjunction
basics
~20 sCOOP: same-origin cuts the document off from cross-origin windows that could hold a reference to it; COEP: require-corp blocks cross-origin subresources that have not opted in. Both are required because crossOriginIsolated means nothing untrusted shares the process — in either direction.
solid answer
~40 sThe two headers close the two directions in which foreign data could reach a document's process. `Cross-Origin-Opener-Policy: same-origin` puts the document in its own browsing-context group, so a cross-origin opener or popup no longer shares a group with it and `window.opener` references are severed — nothing outside can hold a live handle to your window. `Cross-Origin-Embedder-Policy: require-corp` handles the other direction: every cross-origin subresource must opt in, either by sending `Cross-Origin-Resource-Policy: cross-origin` or by being fetched in CORS mode with valid CORS headers; anything else is blocked rather than loaded. `window.crossOriginIsolated` only becomes `true` when both are in force, because the guarantee the platform wants is that nothing in this agent cluster arrived without consent. That guarantee is what re-enables `SharedArrayBuffer`, high-resolution `performance.now()`, and `performance.measureUserAgentSpecificMemory()`.
code
javascript · 5 linesif (self.crossOriginIsolated) {
console.log('isolated: shared memory and fine-grained timers available');
} else {
console.log('not isolated: check COOP and COEP response headers');
}go deeper
Recall the two header names and that both are needed before crossOriginIsolated reads true. Be able to say one governs windows that reference you and the other governs resources you embed.
Explain each header's actual effect: COOP moves the document into its own browsing context group and nulls cross-origin opener references; COEP requires every cross-origin subresource to opt in via CORP or CORS, and blocks the rest.
Demonstrate the threat reasoning — Spectre made same-process residency the risk, so the gate is a conjunction covering both directions — and show you know the rollout cost falls on third-party embeds and popup-based auth flows.
Own the scoping call: whether isolation belongs on a dedicated route or origin rather than site-wide, what the third-party inventory implies, and whether the unlocked capabilities justify constraining every future integration.
## Why the platform withdrew capabilities in the first place Spectre-class transient-execution attacks made it possible for script to read bytes anywhere in its own process address space, regardless of what the language or the same-origin policy allows. Two ingredients make such an attack practical: some cross-origin data resident in the same process, and a clock precise enough to distinguish a cache hit from a miss. Browsers responded by blunting the clock — coarsening `performance.now()` and disabling `SharedArrayBuffer`, which can be turned into an arbitrarily precise timer by spinning a counter in a worker. Those capabilities are genuinely useful, so the platform defined a way to earn them back: prove that nothing unconsenting shares your agent cluster. That proof is called *cross-origin isolation*, and it takes two headers because data can arrive from two directions. ## COOP: cutting the inbound window references ``` Cross-Origin-Opener-Policy: same-origin ``` The default is `unsafe-none`. Under it, a page you open with `window.open()`, or the page that opened you, sits in the same *browsing context group* and can hold a live `window` reference across origins. Even though the same-origin policy limits what that reference exposes, the relationship keeps documents entangled and can keep them in the same process. `same-origin` places the document in a fresh browsing context group unless the other document is same-origin *and* carries a matching COOP. In practice: popups you open become disconnected — you get a window object with no useful cross-origin access — and any `window.opener` from a cross-origin opener is `null`. A third value, `same-origin-allow-popups`, keeps popups you open connected while still severing inbound openers; it is the pragmatic choice for OAuth and payment flows, but it does **not** qualify for cross-origin isolation. ## COEP: gating the outbound embeds ``` Cross-Origin-Embedder-Policy: require-corp ``` The default is again `unsafe-none`. Under `require-corp`, every cross-origin subresource the document loads — images, fonts, scripts, media, frames — must have opted in. There are exactly two ways to opt in: - the response carries `Cross-Origin-Resource-Policy: cross-origin` (or `same-site` where applicable), which is the server saying "I consent to being embedded"; or - the request is made in CORS mode — for example `<img crossorigin="anonymous">` or a `fetch()` without `mode: 'no-cors'` — and the response carries the matching `Access-Control-Allow-Origin`. Anything else fails to load. This is stricter than it first sounds, because the ordinary no-cors image and font loads that make up most third-party content carry neither header by default. A second value, `credentialless`, relaxes this: no-cors cross-origin requests are sent without cookies or other credentials, so no CORP opt-in is required — the reasoning being that an uncredentialled response contains no user-specific data worth stealing. As of 2026, `credentialless` is available in Chromium and Firefox; verify current Safari support before depending on it. Framed documents are the hard case under either value: a nested document must itself send a COEP header, or the embed is blocked. ## Why both, and what "isolated" then means ```js if (self.crossOriginIsolated) { // SharedArrayBuffer, fine-grained timers, memory measurement available } ``` `crossOriginIsolated` is exposed on `Window` and on worker global scopes, and it is `true` only when COOP and COEP are both in force for the document. Either header alone leaves a hole. COOP without COEP: no window holds a handle to you, but you can still pull an arbitrary credentialled cross-origin resource straight into your process. COEP without COOP: every embed consented, but a cross-origin opener still shares a browsing context group with you and may share a process. The capability gate is a conjunction because the threat model is a conjunction. Isolation is also inherited downward, not sideways: a frame can only be cross-origin isolated if every document above it is, and it sends COEP itself. There is no way for one widget to opt itself in on a non-isolated page. ## What it unlocks and what it costs Unlocked: `SharedArrayBuffer` in Chromium and Firefox, the finer-grained `performance.now()` resolution, and `performance.measureUserAgentSpecificMemory()`. The cost is paid by everything you embed and everything that talks to your window. Third-party scripts, analytics beacons, ad frames, embedded video players and font CDNs each need a CORP header you do not control. Auth popups that rely on `window.opener` messaging break under `same-origin`. This is why cross-origin isolation is usually scoped to one route or one dedicated origin that needs the capability, rather than switched on for a whole product. Both headers have report-only siblings — `Cross-Origin-Opener-Policy-Report-Only` and `Cross-Origin-Embedder-Policy-Report-Only` — which surface what *would* break without enforcing anything. Running those first is the only sane way to size the work.
- Does Cross-Origin-Opener-Policy: same-origin-allow-popups qualify a page for cross-origin isolation?No. It severs inbound openers but deliberately keeps popups you open in the same browsing context group, so the isolation guarantee does not hold and `crossOriginIsolated` stays `false`. It exists for pages that must keep an OAuth or payment popup talking back to them, and it is the right value when that flow matters more than the isolated-only capabilities.
- Can a single iframe on an otherwise ordinary page opt itself into cross-origin isolation?No — isolation is inherited from the top-level document down. A frame is cross-origin isolated only if every ancestor is isolated and the frame sends its own COEP header. There is no way to island one widget inside a non-isolated page; a feature that needs `SharedArrayBuffer` has to live under a document tree that is isolated from the top.
- Why did SharedArrayBuffer specifically get gated behind cross-origin isolation rather than simply coarsened?Because it cannot be coarsened. A shared buffer plus a worker incrementing a counter is a homemade clock whose resolution is bounded only by the CPU, so any timer-precision mitigation the browser applies to `performance.now()` is trivially side-stepped. The only workable control was to withhold shared memory until the page proves nothing unconsenting shares its process.
saying these in an interview costs you the question
- Thinking COEP alone is enough for crossOriginIsolated
- Believing COOP blocks embedded images or scripts
- Assuming third-party assets work unchanged under require-corp
- Treating crossOriginIsolated as an origin-wide, not per-document, property
- Calling COOP a clickjacking defence