Debugging Resources Blocked by COEP
This page is a focused procedure under the Cross-Origin Isolation: COOP, COEP & CORP reference. When a page sends Cross-Origin-Embedder-Policy: require-corp, every cross-origin subresource must opt in — and the ones that do not vanish from the page with a distinctive Network-panel error. This is how to identify the blocked resource, decide between adding Cross-Origin-Resource-Policy, switching to credentialless, or proxying, and roll back safely.
Configuration Syntax & Exact Values
The fix is one of two header changes. Either mark the blocked resource embeddable at its origin:
Cross-Origin-Resource-Policy: cross-origin
| CORP value | Effect under a COEP require-corp embedder |
|---|---|
same-origin |
Resource loads only if embedder is the exact same origin; cross-origin embed is blocked. |
same-site |
Loads if embedder shares the registrable domain (scheme-sensitive); cross-site embed blocked. |
cross-origin |
Loads for any embedder. This is the value a public CDN asset needs. |
Or relax the embedder policy on your document so third-party assets load without opting in:
Cross-Origin-Embedder-Policy: credentialless
require-corp demands CORP or CORS from every cross-origin subresource. credentialless loads cross-origin no-cors subresources without credentials (no cookies, no client certs) instead — public assets work unchanged, but any cookie-gated cross-origin resource breaks. Both values still grant crossOriginIsolated.
Server-Side Configuration
Add Cross-Origin-Resource-Policy to the subresource responses (the assets that get blocked), not the document.
Nginx
location ~* \.(woff2|png|jpg|svg|js|css)$ {
add_header Cross-Origin-Resource-Policy "cross-origin" always;
}
always keeps the header on error responses; without it Nginx drops add_header on non-2xx, and a 304/404 asset response would still block.
Apache (mod_headers)
<FilesMatch "\.(woff2|png|jpg|svg|js|css)$">
Header always set Cross-Origin-Resource-Policy "cross-origin"
</FilesMatch>
always applies the header across all status codes; the default onsuccess table skips errors. Requires mod_headers.
Cloudflare (Transform Rules)
Rules → Transform Rules → Modify Response Header → Set static:
When URI Path matches the asset paths
Cross-Origin-Resource-Policy = cross-origin
Use Set, not Add, so the edge does not stack a second CORP header on an origin-set one. Scope to asset paths so you do not attach cross-origin to private API responses.
Node/Helmet
const helmet = require('helmet');
// On the asset-serving app/middleware:
app.use(helmet.crossOriginResourcePolicy({ policy: 'cross-origin' }));
// If you must relax the embedder instead, on the document app:
app.use(helmet.crossOriginEmbedderPolicy({ policy: 'credentialless' }));
Diagnostic & Verification Steps
The signature of a COEP block is a specific Network-panel reason, not a generic CORS error.
Open DevTools → Network, reload, and find the failed request. Its status column shows (blocked) and hovering the request reveals:
net::ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep
The Console typically logs alongside it:
A resource is blocked by OpaqueResponseBlocking / Cross-Origin-Embedder-Policy.
The resource at was blocked because it does not have a
Cross-Origin-Resource-Policy header that allows cross-origin embedding.
Confirm the resource is missing CORP from the command line:
curl -sI https://cdn.third-party.example/font.woff2 | grep -i cross-origin-resource-policy
# No output → the asset sends no CORP and will be blocked under require-corp.
After adding CORP, re-check and confirm isolation in the Console:
curl -sI https://cdn.third-party.example/font.woff2 | grep -i cross-origin-resource-policy
# Expected:
# cross-origin-resource-policy: cross-origin
self.crossOriginIsolated // Expected: true once no subresource is blocked
The Application → Frames → top → Security & isolation panel lists each blocked resource and the reason isolation was denied — the fastest way to enumerate every offender at once.
Edge Cases, Security Implications & Safe Rollback
- CDN images and fonts. Public CDNs increasingly send
Cross-Origin-Resource-Policy: cross-originby default, but many older or self-hosted asset hosts do not. If you control the asset host, add CORP there. If you do not, switch your document tocredentialless(these assets need no cookies) or self-host the asset. - Third-party scripts and widgets. Analytics, ad, and chat scripts loaded cross-origin without CORP are blocked. They rarely accept CORP requests on your behalf, so the practical options are
credentialless(if the script needs no first-party cookies on its own origin) or removing the script from the isolated context. Cookie-dependent widgets break undercredentialless. - Authenticated cross-origin resources. A personalized image or API response gated by a cross-origin cookie cannot use
credentialless— the request goes out without the cookie and returns the wrong (or a 401) response. Proxy it through your own origin so the fetch becomes same-origin, or move it off the isolated route. - same-site is not cross-origin. Setting CORP
same-siteon an asset embedded by a different registrable domain still blocks it. Usecross-originfor genuinely cross-site embedding, and remembersame-siteis scheme-sensitive.
Rollback is non-destructive — COEP gates capabilities, it does not delete state. To restore loading immediately, remove COEP while keeping COOP:
Header unset Cross-Origin-Embedder-Policy
Header always set Cross-Origin-Opener-Policy "same-origin"
This disables SharedArrayBuffer but preserves the opener-severance protection described in the cross-origin isolation reference. Prefer switching to credentialless before a full removal, since that keeps isolation alive.
Frequently Asked Questions
What does NotSameOriginAfterDefaultedToSameOriginByCoep mean?
COEP require-corp defaults every cross-origin subresource to “same-origin only” unless it explicitly opts in. The error means the resource is cross-origin, sent no permitting CORP header and passed no CORS handshake, so the browser blocked it. Add Cross-Origin-Resource-Policy: cross-origin to the resource or switch the embedder to credentialless.
Will switching to credentialless fix every block?
It fixes blocks for cross-origin no-cors assets that do not depend on cookies, because they load anonymously. It will not fix resources that require a cookie cross-origin — those return the wrong response when fetched without credentials. Proxy those through your origin instead.
Can I add CORP to a third-party CDN’s response?
Only if the CDN exposes a setting for it or you proxy the asset through infrastructure you control. You cannot add a header to a response served from someone else’s origin. For uncontrolled assets, use credentialless or self-host.
Does removing COEP also disable my COOP protection?
No. COOP and COEP are independent headers. Removing Cross-Origin-Embedder-Policy only disables crossOriginIsolated (and thus SharedArrayBuffer); keep Cross-Origin-Opener-Policy: same-origin to retain opener-based cross-site-leak defense.
Conclusion
Work incrementally: reproduce the block on staging, enumerate every offending resource in the Network and Security & isolation panels, then resolve each by adding CORP at the source where you can. For assets you do not control, switch the document to credentialless and re-verify self.crossOriginIsolated === true. Only if neither works should you remove COEP — and even then keep COOP same-origin in production.