Setting up cross-origin isolation with COOP and COEP

Cross-origin isolation is an opt-in browser state that unlocks privileged APIs like SharedArrayBuffer, performance.measureUserAgentSpecificMemory(), and higher-precision timer access. The state also blocks document.domain mutations, closing a known gap in the same-origin policy.

To enter this isolated state, serve these headers on the main document:

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

These headers make the browser refuse resources and iframes that haven't explicitly opted in to cross-origin embedding, and prevent cross-origin windows from interacting with your document. Resources loaded from other origins therefore need to carry their own opt-in headers. You can verify the isolated state at runtime with self.crossOriginIsolated.

Terminology

Several similar-sounding policies are involved in isolation; here's what they stand for:

  • COEP — Cross Origin Embedder Policy
  • COOP — Cross Origin Opener Policy
  • CORP — Cross Origin Resource Policy
  • CORS — Cross Origin Resource Sharing
  • CORB — Cross Origin Read Blocking

Enabling isolation step by step

1. Send Cross-Origin-Opener-Policy: same-origin

With COOP: same-origin on the top-level page, the document is placed in its own browsing context group, separate from any windows it opens or that open it, unless those windows share the same origin and the same COOP setting. That separation disables direct communication between the document and windows it no longer shares a context group with.

A browsing context group is a cluster of windows that can reference one another, such as a top-level page and the documents inside its iframes. If https://a.example opens a dialog at https://b.example, both windows normally share the same browsing context and can interchange references through DOM APIs like window.opener.

Illustration representing Browsing Content Group with user interacting with page elements.

DevTools shows whether a window opener and its opened window ended up in separate context groups.

2. Serve CORP or CORS on all resources

Every subresource in the page must be loadable under either CORP or CORS before you enable COEP. Set headers based on the resource's intended audience:

  • Same origin only — use Cross-Origin-Resource-Policy: same-origin.
  • Same site, possibly cross origin — use Cross-Origin-Resource-Policy: same-site.
  • Under your control, cross origin — use Cross-Origin-Resource-Policy: cross-origin when possible.
  • Cross origin, not under your control — add the crossorigin attribute to the loading tag if the resource supports CORS (for example, <img src="..." crossorigin>), or ask the owner to send CORS or CORP headers.
  • Iframes — apply the same privacy rules: cross-origin, same-site, or same-origin depending on context.
  • Workers — scripts for WebWorker must be same-origin, so CORP/CORS headers aren't needed.
  • Under COEP: require-corp — any cross-origin subresource loaded without CORS must send Cross-Origin-Resource-Policy: cross-origin to be embeddable. This applies to <script>, importScripts, <link>, <video>, <iframe>, and more.

Isolation inside iframes

A frame embedded with <iframe allow="cross-origin-isolated"> can have its own cross-origin isolated state, but the whole document chain — parent and child frames — must also be isolated.

3. Dry run with the Report-Only header

Before enforcing COEP, test with Cross-Origin-Embedder-Policy-Report-Only. This header reports violations without blocking any content. Apply it to every document in the chain — top-level page, iframes, and worker scripts — and watch for issues using the Reporting API.

4. Enable COEP

Once resource headers are confirmed and no Report-Only violations remain, swap Cross-Origin-Embedder-Policy-Report-Only for the enforcement version: Cross-Origin-Embedder-Policy. Use the same value on all documents, including embedded frames and workers.

Verifying the result

When isolation is active, self.crossOriginIsolated returns true. This confirms the page and all of its windows and resources are inside the same isolated browsing context group, and APIs such as performance.measureUserAgentSpecificMemory() are available.

Troubleshooting

Using Chrome DevTools

Visually blocked resources like images are easy to spot: the request fails and a broken image renders. For invisible resources, like scripts or stylesheets, open the Network panel. COEP failures appear in the Status column with (blocked:NotSameOriginAfterDefaultedToSameOriginByCoep).

COEP issues in the Status column of the Network panel.

Clicking the entry exposes more detail. The Application panel's Frames section, under the "top" expandable, shows per-frame isolation status, including availability of SharedArrayBuffer, and the same for dialogs opened from your page.

Details of the COEP issue are shown in the Headers tab after clicking a network resource in the Network panel.

Chrome DevTools iframe inspector.

Chrome DevTools dialog window inspector

Watching reports with the Reporting API

The Reporting API lets a browser send a report whenever COEP blocks a resource or COOP isolates a window. Chrome has supported the API since version 69. Configure a report endpoint and server to receive these events. Example report payloads for blocked cross-origin resources and opened-in-isolation dialogs look as follows:

[{
  "age": 25101,
  "body": {
    "blocked-url": "https://third-party-test.glitch.me/check.svg?",
    "blockedURL": "https://third-party-test.glitch.me/check.svg?",
    "destination": "image",
    "disposition": "enforce",
    "type": "corp"
  },
  "type": "coep",
  "url": "https://cross-origin-isolation.glitch.me/?coep=require-corp&coop=same-origin&",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4249.0 Safari/537.36"
}]
[{
  "age": 7,
  "body": {
    "disposition": "enforce",
    "effectivePolicy": "same-origin",
    "nextResponseURL": "https://third-party-test.glitch.me/popup?report-only&coop=same-origin&",
    "type": "navigation-from-response"
  },
  "type": "coop",
  "url": "https://cross-origin-isolation.glitch.me/coop?coop=same-origin&",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4246.0 Safari/537.36"
}]

In Report-Only mode, COOP also signals attempts to access across different browsing context groups — for instance, a postMessage() call would generate a report like this:

[{
  "age": 51785,
  "body": {
    "columnNumber": 18,
    "disposition": "reporting",
    "effectivePolicy": "same-origin",
    "lineNumber": 83,
    "property": "postMessage",
    "sourceFile": "https://cross-origin-isolation.glitch.me/popup.js",
    "type": "access-from-coop-page-to-openee"
  },
  "type": "coop",
  "url": "https://cross-origin-isolation.glitch.me/coop?report-only&coop=same-origin&",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4246.0 Safari/537.36"
},
{
  "age": 51785,
  "body": {
    "disposition": "reporting",
    "effectivePolicy": "same-origin",
    "property": "postMessage",
    "type": "access-to-coop-page-from-openee"
  },
  "type": "coop",
  "url": "https://cross-origin-isolation.glitch.me/coop?report-only&coop=same-origin&",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4246.0 Safari/537.36"
}]

Cross-origin isolation is the combination of these COOP and COEP headers, with self.crossOriginIsolated acting as the final check that the environment is set up correctly.

Changelog highlights

Cross-origin isolation has evolved significantly since its initial rollout. The following updates track the key changes to browser support and API availability:

  • June 21, 2022: Worker scripts also need care when cross-origin isolation is enabled.
  • Aug 5, 2021: The JS Self-Profiling API has been removed from the list of APIs requiring cross-origin isolation, following a change in its specification direction.
  • May 6, 2021: The restriction timeline for SharedArrayBuffer on non-cross-origin-isolated sites was adjusted for Chrome M92 based on community feedback.
  • April 16, 2021: Notes were added regarding COEP credentialless mode and the relaxed condition allowing COOP same-origin-allow-popups for cross-origin isolation.
  • March 5, 2021: Removed limitations for SharedArrayBuffer, performance.measureUserAgentSpecificMemory(), and debugging in Chrome 89. Higher precision for performance.now() and performance.timeOrigin was listed as an upcoming capability.
  • February 19, 2021: Added details on the feature policy allow="cross-origin-isolated" and corresponding DevTools debugging support.
  • October 15, 2020: self.crossOriginIsolated became available in Chrome 87. When it returns true, document.domain is immutable. performance.measureUserAgentSpecificMemory() ended its origin trial and is enabled by default in Chrome 89. SharedArrayBuffer on Android Chrome became available from Chrome 88.