Cross-origin isolation is a browser security state that locks your page into its own isolated process, preventing other origins from sharing memory or accessing timing data that could be exploited in Spectre-style side-channel attacks. A page enters the cross-origin isolated state when it sends two HTTP response headers together: Cross-Origin-Opener-Policy: same-origin (COOP) and Cross-Origin-Embedder-Policy: require-corp (COEP). The full mechanics are defined in the HTML Living Standard (Origin section) and explained in Google's web.dev cross-origin isolation guide.
The origin of this feature is the Spectre vulnerability, publicly disclosed on January 3, 2018 by Google's Project Zero and academic researchers. Spectre demonstrated that a malicious script running in the same process as another tab could use high-resolution timers to infer the contents of memory belonging to that tab — including passwords, session tokens, and private data. Browsers responded by disabling SharedArrayBuffer and reducing timer precision globally. Cross-origin isolation re-enables those powerful APIs safely, by guaranteeing that only same-origin content shares the same process.
Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp.SharedArrayBuffer, performance.measureUserAgentSpecificMemory(), and high-resolution timers.self.crossOriginIsolated returns true when the state is active.Cross-Origin-Resource-Policy header or CORS.Spectre is a class of hardware vulnerability that affects most modern CPUs manufactured since the mid-1990s. It exploits speculative execution — a performance optimisation where the CPU runs instructions ahead of time and discards the results if they turn out to be unnecessary. An attacker can use carefully timed memory access patterns to infer what data the CPU was speculating on, even data it was not supposed to access.
In a browser, Spectre becomes dangerous when two different origins share the same OS process. A malicious script on evil.example in one tab could, in theory, use a SharedArrayBuffer as a high-resolution timer to read memory from yourbank.com open in another tab in the same process. All major browser vendors confirmed this class of attack as a realistic threat in 2018.
Browsers initially mitigated Spectre by disabling SharedArrayBuffer entirely and reducing timer precision across all pages. Cross-origin isolation is the long-term solution: by placing each cross-origin isolated page in its own dedicated process, the attack surface disappears — there is no shared memory to leak.
COOP controls the relationship between your page and any windows or tabs that open it or that it opens. By severing the browsing context group between cross-origin windows, COOP prevents other origins from accessing your page's window object — and from sharing its process.
| Value | Meaning | Use when |
|---|---|---|
unsafe-none | Default. No restriction — all windows share a browsing context group | Legacy behaviour; not recommended for security-sensitive pages |
same-origin | Only same-origin pages can share a browsing context group with your page | Required for full cross-origin isolation (strictest) |
same-origin-allow-popups | Like same-origin but retains access to popups your page opens to cross-origin URLs | When your page needs to open third-party popups but does not need cross-origin isolation |
restrict-properties | Severs script access between cross-origin windows but preserves limited interactions like postMessage and closed. Enables cross-origin isolation when paired with COEP. | Modern recommended path for sites that need OAuth/payment popups and cross-origin isolation (Chrome 116+) |
noopener-allow-popups | Severs the opener relationship even for same-origin documents while still allowing popups to be opened | When the opener should be fully isolated but your page must still open new windows |
# Strictest — full isolation, no popup interaction
Cross-Origin-Opener-Policy: same-origin
# Modern path — isolation + safe OAuth/payment popups (Chrome 116+)
Cross-Origin-Opener-Policy: restrict-properties
# When you need popups but not full isolation
Cross-Origin-Opener-Policy: same-origin-allow-popupsSee the MDN Cross-Origin-Opener-Policy reference and Chrome's restrict-properties announcement for current per-value browser support.
COEP controls which cross-origin resources the page is allowed to load. It ensures that every cross-origin resource explicitly opts in to being loaded — preventing your page from inadvertently pulling in resources from origins that have not consented, which could otherwise be used to leak information.
| Value | Meaning | Use when |
|---|---|---|
unsafe-none | Default. Cross-origin resources load without any opt-in requirement | Legacy behaviour; does not enable cross-origin isolation |
require-corp | All cross-origin resources must send a Cross-Origin-Resource-Policy header or CORS headers | Required for cross-origin isolation (strictest) |
credentialless | Cross-origin no-CORS requests are sent without credentials; resources load without needing CORP | Easier migration path when you cannot control third-party resource headers |
# Strictest — all cross-origin resources must opt in
Cross-Origin-Embedder-Policy: require-corp
# More permissive — useful when third-party resources don't send CORP
Cross-Origin-Embedder-Policy: credentiallessWhen your page uses COEP: require-corp, every cross-origin resource it loads must respond with a Cross-Origin-Resource-Policy (CORP) header — otherwise the browser blocks it. This header is set on the server that serves the sub-resource (images, scripts, fonts, API responses), not on your page.
| CORP value | Who can load this resource |
|---|---|
same-origin | Only the same origin |
same-site | Same site (subdomains included) |
cross-origin | Any origin — required for public CDN assets that any site can embed |
# On a public CDN serving images or fonts to any site
Cross-Origin-Resource-Policy: cross-origin
# On an API that should only be called from the same site
Cross-Origin-Resource-Policy: same-siteIf you cannot modify the headers on a third-party resource (e.g. a public CDN you do not control), use COEP: credentialless instead of require-corp — it lifts the CORP requirement for no-credentials requests while still enabling cross-origin isolation.
To put your page into the cross-origin isolated state, send both COOP and COEP headers together on every response for that page:
# Full cross-origin isolation — strictest option
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
# Easier to deploy when you use third-party resources
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: credentialless
# Modern path — preserves OAuth/payment popups (Chrome 116+)
Cross-Origin-Opener-Policy: restrict-properties
Cross-Origin-Embedder-Policy: credentiallessAfter setting these headers, verify the state in the browser console:
// In the browser console — should return true
console.log(self.crossOriginIsolated); // trueIf self.crossOriginIsolated returns false, one or both headers are missing, or a sub-resource is blocking the COEP requirement. Open Chrome DevTools → Application → Frames to see which resources are failing the COEP check.
Cross-origin isolation is not just a defensive measure — it is a prerequisite for several high-performance browser APIs that were disabled after Spectre:
SharedArrayBuffer — enables true shared memory between the main thread and web workers, required for high-performance WebAssembly applications, audio worklets, and multi-threaded games.performance.now()) — full precision is restored under cross-origin isolation. Without it, browsers reduce timer precision (Chrome to roughly 100µs, Firefox historically to 1–2ms) to mitigate Spectre-style timing attacks.performance.measureUserAgentSpecificMemory() — lets the page measure its own memory usage, useful for detecting memory leaks in long-running applications.See web.dev: Why you need cross-origin isolated for powerful features for the complete capability list.
If your page embeds a third-party iframe (an ad, a video player, a map widget) and that third party does not send Cross-Origin-Resource-Policy: cross-origin, the iframe will be blocked. Either switch to COEP: credentialless, ask the third party to add the CORP header, or remove the embed.
Many OAuth flows open a popup to a third-party identity provider and then communicate back to the opener via window.opener. With COOP: same-origin, the opener reference is severed — window.opener is null in the popup. The modern fix is COOP: restrict-properties (Chrome 116+), which preserves cross-origin isolation while still allowing safe interaction primitives like postMessage and closed. If you cannot rely on Chromium-only support yet, switch to COOP: same-origin-allow-popups (loses isolation) or migrate to redirect-based OAuth flows.
Images and fonts loaded from a CDN without a Cross-Origin-Resource-Policy header will be blocked under require-corp. Ask the CDN provider to add Cross-Origin-Resource-Policy: cross-origin to its responses, or use COEP: credentialless which waives the CORP requirement for unauthenticated requests.
COOP and COEP must be set on the HTML document response — not on sub-resources. Setting them only on an API endpoint has no effect. Apply them globally to all HTML responses, then handle any per-route exceptions for pages that embed third-party iframes.
self.crossOriginIsolated — returns true when the page is correctly isolated.Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy appear on the HTML document response.Not strictly required, but still recommended for security-sensitive pages. Cross-origin isolation prevents other origins from sharing your page's process, reducing the attack surface for Spectre-style attacks even if you are not using shared memory APIs. For most sites, the bigger motivation is enabling SharedArrayBuffer for performance-intensive features.
It can. Scripts loaded via <script> tags are not affected by COEP, but iframes and certain cross-origin fetches may be blocked under require-corp. Using credentialless instead of require-corp reduces breakage significantly. Test in report-only mode if your tag manager loads iframes.
Yes. Cross-Origin-Opener-Policy-Report-Only and Cross-Origin-Embedder-Policy-Report-Only send violation reports to a report-to endpoint without enforcing the policy. This lets you observe what would break before switching to the enforcing headers — the same pattern as Content-Security-Policy-Report-Only.
CORS (Cross-Origin Resource Sharing) controls whether a browser allows a script on one origin to read the response from another origin — it is opt-in by the resource server and affects JavaScript-initiated fetches. CORP (Cross-Origin-Resource-Policy) controls whether the browser is allowed to load a resource at all into a cross-origin document — it is required by COEP and applies to all resource types including images and fonts, not just script-initiated fetches.
Not directly. The headers are security controls — they do not affect how search engine crawlers index your content or how quickly the page loads. Indirectly, enabling SharedArrayBuffer and high-resolution timers can unlock performance improvements in compute-heavy applications (WebAssembly, audio processing), which may improve user experience metrics.
Yes. COOP and COEP are per-response headers — you can enable cross-origin isolation only on the specific routes that need it (e.g. a WebAssembly-powered editor or a shared-memory audio worklet page) while leaving the rest of the site unaffected. Just ensure that every HTML response for those specific routes includes both headers.
Cross-origin isolation is the browser's answer to Spectre: two HTTP headers — Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp — that place your page in its own dedicated process, away from other origins. Beyond the security benefit, it unlocks SharedArrayBuffer and high-resolution timers for performance-intensive applications. The main adoption hurdle is ensuring that cross-origin sub-resources opt in via CORP headers; switching to credentialless mode reduces that friction significantly. Run a Greadme deep scan to check whether your COOP and COEP headers are present and correctly configured.