Cross-Origin-Embedder-Policy security response
Requires all cross-origin resources loaded by this page to explicitly opt in — needed alongside COOP to enable SharedArrayBuffer.
What it does
Cross-Origin-Embedder-Policy (COEP) requires that every cross-origin resource your page loads has explicitly opted in to cross-origin loading — either via Cross-Origin-Resource-Policy: cross-origin or via CORS. If a resource hasn't opted in, the browser blocks it.
Combined with Cross-Origin-Opener-Policy: same-origin, COEP enables cross-origin isolation — the prerequisite for re-enabling SharedArrayBuffer, high-precision performance.now(), and other APIs restricted after the Spectre vulnerability.
Syntax
Cross-Origin-Embedder-Policy: unsafe-none
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Embedder-Policy: credentialless
Values
unsafe-none (default)
No restrictions. Cross-origin resources load normally regardless of whether they set CORP or CORS. Standard browser behaviour. Does not contribute to cross-origin isolation.
require-corp
Every cross-origin resource loaded by this page must either:
- Set
Cross-Origin-Resource-Policy: cross-origin, or - Have a CORS header (
Access-Control-Allow-Origin) matching your origin
If a resource doesn't comply, the browser blocks it and you get a network error. This is the value needed for cross-origin isolation.
credentialless
A less disruptive middle path introduced to ease COEP adoption. Cross-origin resources that don't have CORP or CORS can still load — but their requests are sent without cookies or credentials. This means authenticated cross-origin resources won't work (they'll return empty/error responses without auth), but unauthenticated public resources load fine.
Supported in Chrome/Edge. Firefox support arrived later. Use require-corp for maximum compatibility; use credentialless if transitioning and have many unauthenticated third-party resources.
Enabling cross-origin isolation
To get self.crossOriginIsolated === true:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Both must be on the response for the top-level document. Verify it worked:
console.log(self.crossOriginIsolated); // true
const sab = new SharedArrayBuffer(1024); // works
What gets blocked by require-corp
Every third-party resource without CORP or CORS is blocked. Common culprits:
- Google Fonts — historically loaded without CORP (now fixed: they set
cross-origin) - CDN-hosted scripts (Bootstrap, jQuery CDN) — many older CDN setups lack CORP
- Embedded YouTube/Vimeo players — iframes may not cooperate
- Analytics pixels — third-party tracking images
- Payment widgets (Stripe, PayPal iframes) — most now support COEP
- Social share buttons — inline scripts from social platforms
Auditing before enabling
Check what would break before going live:
Use Chrome DevTools:
- Open DevTools → Network tab
- Add
Cross-Origin-Embedder-Policy: report-onlyheader (the report-only version) - Load your page
- Look for requests blocked with COEP errors in the console
Use the report-only variant:
Cross-Origin-Embedder-Policy-Report-Only: require-corp; report-to coep-endpoint
Collect reports of what would be blocked without actually breaking anything.
COEP and iframes
COEP propagates into iframes: if your top-level document sets COEP: require-corp, embedded iframes that don't cooperate are blocked. For iframes to work under COEP, they need to either:
- Set
COEP: require-corpthemselves (and all their resources must comply) - Be same-origin
- Set
CORP: cross-origin(for the iframe resource itself)
This is why many embedding scenarios (payment widgets, chat widgets, video players) require coordination between your site and the third-party to support COEP.
Common mistakes and gotchas
Enabling COEP without auditing third-party resources. This is the #1 COEP mistake. Your analytics, CDN scripts, fonts, maps embeds, and payment widgets all need to cooperate. Enable report-only first, collect reports, fix everything, then switch to enforce.
Forgetting COEP needs COOP too. COEP alone does not enable cross-origin isolation. You need both COEP: require-corp and COOP: same-origin.
Using require-corp when credentialless would do. If your page loads lots of unauthenticated public resources (fonts, images, scripts) that don't have CORP, credentialless may unblock you without needing to chase every CDN provider for CORP header support.
Real-world examples
Full cross-origin isolation setup:
HTTP/1.1 200 OK
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Transition mode (monitoring before enforcing):
HTTP/1.1 200 OK
Cross-Origin-Embedder-Policy-Report-Only: require-corp
Credentialless for public-resource-heavy pages:
HTTP/1.1 200 OK
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: credentialless
Cross-origin resource opting in (needed for COEP pages to load it):
HTTP/1.1 200 OK
Content-Type: font/woff2
Cross-Origin-Resource-Policy: cross-origin
Access-Control-Allow-Origin: *
FAQ
Does COEP affect server-side rendering?
No — COEP is a browser policy that governs subresource loading on the client side. SSR happens on the server where there's no browser COEP enforcement. COEP on the response instructs the browser about how to handle cross-origin resources as it renders the page.
What's the difference between COEP and CSP?
CSP (script-src, img-src, etc.) restricts which origins resources can come from. COEP restricts whether cross-origin resources have opted in to being loaded cross-origin. They're orthogonal: CSP could allow https://cdn.example.com but COEP would still block resources from there if they don't have CORP: cross-origin.
Can I use COEP on just some pages?
Yes — COEP is per-response. You can enable it on pages that need SharedArrayBuffer (e.g., a video editing page, a WebAssembly app) without enabling it site-wide. Make sure those specific pages' resources all cooperate.
Fun fact
The credentialless value was invented specifically because require-corp proved too disruptive for real-world adoption. After Spectre in 2018, browser vendors wanted to restore SharedArrayBuffer functionality but found that require-corp was a major barrier — too many third-party resources didn't cooperate. credentialless was a pragmatic compromise: strip credentials from uncooperative cross-origin loads (making them harmless from a data-leak perspective) rather than blocking them entirely. It represents a common pattern in web standards where the theoretically correct solution (require-corp) gets a pragmatic companion (credentialless) to ease real-world migration.