GuidesDIAGNOSE

Why no-cors returns an opaque response

Setting mode: 'no-cors' removes the console error by altering the request contract. JavaScript receives an opaque response that hides the body, status, and headers. Reading the data requires a permitting CORS configuration or an alternate routing path.

What an opaque response hides

Using mode: 'no-cors' informs the browser to treat the request like an HTML form submission or media embed. The browser suppresses CORS validation errors because it promises client scripts nothing in return. Network traffic executes, but the JavaScript runtime receives a blank container.

  • response.status returns 0 instead of the HTTP status code sent by the origin server.
  • response.ok evaluates to false because the browser cannot verify success.
  • response.body produces an empty byte stream, and parsing methods like response.json() reject.
  • response.headers exposes an empty header map that omits all upstream response headers.
  • response.type reports opaque, masking any HTTP redirects that occurred during the transfer.

DevTools shows the wire traffic while JavaScript gets nothing

Browser developer tools display the actual HTTP transaction occurring over the network layer. A developer sees an HTTP 200 status and raw JSON payloads inside the Network panel while the fetch promise yields an empty shell. Developer tools observe raw wire bytes, whereas browser execution engines enforce cross-origin isolation policies on script access.

no-cors hides the response, not the error
// What not to do
const hidden = await fetch(url, { mode: 'no-cors' });
hidden.status; // 0
await hidden.text(); // '' — always empty

// What to do: default mode plus a response path that grants permission
const response = await fetch(url);
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const data = await response.json();

Three working paths for reading the data

Reading the response payload requires resolving the origin policy instead of suppressing the network check. The appropriate path depends on whether you control the target backend or consume an external endpoint.

  • Configure your backend: Add Access-Control-Allow-Origin headers to the server you manage, specifying your client domain or wildcard permissions according to your security requirements.
  • Remove the mode override: External APIs that already emit CORS headers fail when callers restrict the request. Switch back to the default mode: 'cors' and verify headers using the header checker.
  • Route public data through a proxy: Third-party endpoints lacking CORS headers can be accessed by prefixing target URLs with https://proxy.cors.dev/ on any public host. Because the proxy operator can observe traffic routed through it, this routing path serves public data only.

Valid uses for no-cors requests

Opaque responses serve narrow technical workflows where script inspection is unnecessary. Analytics platforms dispatch fire-and-forget telemetry beacon pings without waiting for return data. Service workers use mode: 'no-cors' to populate the Cache API with third-party stylesheets or images before rendering elements consume those resources directly. Any architecture requiring client code to read JSON, text, or HTTP headers cannot use an opaque response.

Good questions.

Can I read the status code of an opaque response?

No. The Fetch spec forces response.status to 0 for opaque responses. Incoming wire traffic carries the true status code to the browser engine, but script environments cannot read it.

Is no-cors ever the right fix for a JSON API?

No. JSON APIs require script execution environments to parse the body text. Because an opaque response zeroes the body and status, response.json() fails immediately.

Does no-cors skip the preflight?

Yes. Setting mode: 'no-cors' restricts outgoing requests to CORS-safelisted methods like GET, HEAD, and POST with safelisted request headers. These requests match legacy form submissions, so browsers bypass the OPTIONS preflight check described in the preflight guide.