GuidesDIAGNOSE

Access to fetch has been blocked by CORS policy

When Chrome logs that Access to fetch has been blocked by CORS policy, the network request reached the destination, but the browser withheld the response from your script because the response headers failed validation against the page origin.

The exact console message

Chrome console
Access to fetch at 'https://api.example.com/data' from origin 'https://your-site.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Chrome prints this specific wording for fetch requests, while Firefox prints its own separate phrasing for the same CORS failure.

What the browser is telling you

The message appears in two situations. For a request the browser could send directly, the response arrived, but its headers failed the origin check, so the browser withheld the body from your script. For a request that required a preflight, the OPTIONS exchange failed and the real request was never sent at all.

The text after CORS policy: is the trailing clause, and it names the exact failure condition. Reading the clause before changing any configuration saves guesswork.

Decode the trailing clause

Chrome uses a fixed prefix and attaches one of several trailing clauses depending on the exact header state. Calls made through XMLHttpRequest print the identical trailing clauses with an Access to XMLHttpRequest prefix.

Chrome CORS console clauses and their causes
Console clauseCauseFix layer
Header missingChrome prints: No 'Access-Control-Allow-Origin' header is present on the requested resource. The response arrived without the header. The server omitted it or an intermediary stripped it.API server or reverse proxy.
Wrong origin valueChrome prints: The 'Access-Control-Allow-Origin' header has a value '<value>' that is not equal to the supplied origin. The server sent a fixed or stale origin value that does not match the scheme, host, and port of the page.API server origin configuration.
Wildcard with credentialsChrome prints: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The request included credentials or credentials mode was set to include, but the server returned a wildcard.API server CORS configuration.
Preflight check failed (wrapper)Chrome prints: Response to preflight request doesn't pass access control check: <inner clause> A non-safelisted request triggered an OPTIONS preflight that returned non-2xx status, a redirect, or missing headers.OPTIONS routing and auth middleware.
Duplicate header valuesChrome prints: The 'Access-Control-Allow-Origin' header contains multiple values '<values>', but only one is allowed. Two separate layers emitted the header independently, such as an application middleware and a web server.Server or proxy header deduplication.

Fix the layer that emits the response

The resolution must occur at the layer generating the response: the API application, a reverse proxy in front of it, or an intermediary routing service. Client fetch options cannot inject response headers into an incoming payload.

  1. Add the missing header

    Configure the server to send Access-Control-Allow-Origin on all HTTP responses, including 4xx and 5xx error responses.

  2. Correct the origin value

    Update the server configuration to match the exact calling origin, including scheme, hostname, and port, or dynamically echo the validated Origin request header.

  3. Replace the wildcard for credentialed requests

    When sending cookies or authorization headers, configure the server to echo the exact requesting origin instead of *, and add Access-Control-Allow-Credentials: true.

  4. Fix preflight handling

    Ensure OPTIONS requests return a 2xx status code without redirects, and bypass any authentication middleware that rejects unauthenticated preflight calls.

  5. Deduplicate emitted headers

    Remove duplicate Access-Control-Allow-Origin directives across your application code, web server configurations like nginx, and content delivery networks.

Setting mode: 'no-cors' in the client fetch options does not solve the problem. It suppresses the console error by placing the response into an opaque state, which renders the response body, headers, and status code completely unreadable to JavaScript.

If the destination is an external third-party API that you cannot modify, you can route GET and HEAD requests through a public CORS proxy. This applies strictly to public data, because proxy operators can inspect traffic, and credentials such as cookies and authorization headers are stripped by design. Prefix the target URL with the proxy endpoint.

Route a third-party public API through cors.dev
// Before: the API sends no CORS headers, so the browser blocks the read.
const direct = await fetch('https://jsonplaceholder.typicode.com/todos/1');

// After: prefix the URL; cors.dev answers with the headers your origin needs.
const response = await fetch(
  'https://proxy.cors.dev/https://jsonplaceholder.typicode.com/todos/1',
);
const data = await response.json();

// Public data only: no keys, no cookies, GET and HEAD to public HTTPS hosts.

Verify the fix

Reproduce the exact HTTP transaction from the command line using curl. Supply the Origin header that matches your front-end application to inspect the raw response headers returned by the server.

Reproduce the browser's origin check
$ curl -i https://api.example.com/data \
    -H "Origin: https://your-site.com"

HTTP/2 200
content-type: application/json

# No access-control-allow-origin line in this answer: that is the bug
# Chrome reports. curl itself does not care, which is why it "works" here.

You can also paste the resulting raw HTTP headers directly into the CORS header checker to validate the origin match and preflight directives.

Good questions.

Why does the request succeed in Postman but fail in the browser?

Postman is an API testing client, not a web browser. It does not enforce the browser same-origin policy, does not send standard browser security headers, and does not block JavaScript from reading responses when CORS headers are missing.

Does this error mean the destination server rejected my request?

Not necessarily. In many cases, the server processed the request and returned a valid response body. The browser received the response over the network but refused to hand the data to your JavaScript code because the required CORS response headers were absent or invalid.

Can I resolve this error by setting mode: 'no-cors' in fetch?

No. The mode: 'no-cors' option silences the CORS check by downgrading the response to an opaque type. Your code will receive a response object with a status of 0, an empty header set, and a null body, preventing your application from reading the data.

Why does the API work on localhost but fail in production?

Local development setups often run behind a development server proxy that forwards requests from the same origin. When deployed to production, the browser makes direct cross-origin requests to the remote API host, which fails if the production server lacks proper CORS headers.