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.

Decision tree for a COEP-blocked resource A decision tree: if you can add CORP to the resource, add it; otherwise switch the embedder to credentialless if the resource needs no cookies; otherwise proxy it through your own origin. Resource blocked by COEP require-corp Control the resource's origin? yes no Add CORP: cross-origin (or CORS) Needs cookies? no → credentialless Needs cookies: proxy via your origin
Prefer adding CORP at the source; if you cannot, switch to credentialless for cookie-free assets, and proxy anything that must carry credentials cross-origin.

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

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.