EMZETT.
Login

CORS error: blocked by CORS policy

In short: The browser blocks the response from a foreign origin because the server has not explicitly allowed this page to access it.

In more detail: Message in the console: Access to fetch at 'https://api.example.com' from origin 'https://my-site.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. This is a protection of the browser, not the server: the request arrived but the page may not read the response.

In Depth

Why it exists

The same-origin policy prevents a page (origin A) from reading data from origin B. An origin consists of protocol, host and port. CORS lets server B give selected origins access via headers. For “non-simple” requests (JSON with Authorization, PUT/DELETE) the browser first sends a preflight request (OPTIONS).

Typical causes

  • The server sends no Access-Control-Allow-Origin.
  • The origin does not match (http vs. https, www vs. without, other port, typo).
  • The preflight (OPTIONS) is not answered or returns 404/401.
  • Access-Control-Allow-Headers or -Methods do not contain what is sent.
  • With cookies/credentials Access-Control-Allow-Origin must not be * and Access-Control-Allow-Credentials: true must be set.
  • An error page (500/502) contains no CORS headers and disguises itself as a CORS error.

Solutions

  1. On the server set the allowed origin: Access-Control-Allow-Origin: https://my-site.com and if needed -Methods, -Headers, -Max-Age. (Express: cors package; Next.js: headers in route handlers/next.config.)
  2. Answer the preflight OPTIONS with 204.
  3. In development use a proxy (Vite/Next rewrites) so the browser only addresses its own origin.
  4. Do not use * together with cookies; check origins against a list of allowed origins and reflect them back.
  5. Look at the Network tab (F12): which response and which headers really came back? Browser CORS extensions only “solve” your local problem, not the users’.

See also: CORS, HTTP headers