CORS: how it works, and how it is misconfigured

CORS (cross-origin resource sharing) is how a server tells browsers that pages from other origins may read its responses. By default a browser lets a page read only responses from its own origin, the same-origin policy; CORS headers relax that rule for chosen origins. A CORS misconfiguration relaxes it too far, so any website a signed-in user visits can read that user's data from your API.

Section Web attacks Updated Weakness CWE-942

Quick facts

Stands for
Cross-origin resource sharing
Defined in
The WHATWG Fetch Standard; origins in RFC 6454
Weakness
CWE-942: permissive cross-domain policy with untrusted domains
OWASP category
A02:2025 Security Misconfiguration, which lists CWE-942 (A02 page); see security misconfiguration
Does a WAF stop it?
No. CORS is decided by response headers and enforced by the browser

A practical walk-through of the headers is on MDN.

The same-origin policy, and what CORS changes

An origin is the combination of scheme, host and port: https://shop.example and https://api.example are different origins. Browsers let a page send requests to any origin, but read the response only from its own. That rule is what stops a random website from reading your email or bank balance through your signed-in browser.

CORS is a controlled exception. The server answers with headers naming which origins may read the response, and the browser enforces it. CORS never protects the server: it decides what pages in a browser may read, and tools outside a browser ignore it.

How CORS works

  • Origin. The browser adds this header to cross-origin requests, naming the page's origin.
  • Access-Control-Allow-Origin. The server names one origin that may read the response, or * for any origin.
  • Access-Control-Allow-Credentials: true. Allows the response to be read when the request carried cookies; browsers refuse this together with *.
  • Preflight request. For methods other than GET, HEAD and simple POST, or with custom headers, the browser first sends an OPTIONS request and proceeds only if the answer allows it.
  • Access-Control-Allow-Methods and -Headers. The preflight answer listing what the real request may use.

If the response names a different origin, or none, the browser still receives it but refuses to hand it to the page. That refusal is what developers see as a CORS error.

What a CORS error means

A message such as “blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present” means the browser did its job: the server did not say the page's origin may read the response. The request may well have reached the server and run; only the reading was blocked.

The fix is on the server that owns the resource: return the right Access-Control-Allow-Origin for the origins that legitimately call it, and answer preflight requests. Browser extensions that switch CORS off, or a proxy that adds * to everything, make the error disappear by removing the protection.

Dangerous CORS misconfigurations

  • Reflecting any Origin with credentials. The server copies whatever arrives in Origin into the allow header and adds Allow-Credentials: true. Any website can now read a signed-in user's data.
  • Loose matching. A check that the origin ends with example.com also accepts attacker-example.com; one that it starts with it accepts example.com.attacker.net.
  • Allowing the null origin. Sandboxed frames and local files send Origin: null, and attackers can produce it on purpose.
  • Trusting every subdomain. One forgotten subdomain with an XSS flaw then becomes a way to read the main API.
  • Allowing plain HTTP origins. An attacker on the network can inject script into the HTTP page, which the HTTPS API then trusts.

A wildcard * on public data that needs no sign-in is fine. The danger is always in combining a permissive origin with responses that depend on who the user is.

WHICH CORS SETUPS LEAK ANY SITE CAN READ USER DATA EXPOSED SAFE * ON PUBLIC DATA REFLECTED ORIGIN + CREDENTIALS SUFFIX MATCH + CREDENTIALS NULL ORIGIN + CREDENTIALS EXACT ALLOWLIST // the risk is a permissive origin together with credentials, not the wildcard itself
// five setups, and which of them expose signed-in users

CORS is not a CSRF defence

CORS controls reading, not sending. A cross-site form can still submit to your site, and the browser will still attach cookies according to their SameSite setting; CORS only decides whether the attacker's page can see the answer. Protection against forged actions comes from CSRF tokens and SameSite cookies.

A strict CORS policy does make some requests harder to forge, because a preflight stops JSON requests with custom headers from other origins. That is a side effect, not a design you should rely on.

How to configure CORS safely

  1. Keep an explicit allowlist. Compare the full origin, scheme and port included, against a fixed list.
  2. Send credentials only where needed. Most cross-origin calls do not need cookies; leave Allow-Credentials off unless they do.
  3. Never reflect Origin unchecked, and never allow null. Both are equivalent to allowing everyone.
  4. Add Vary: Origin. When the allow header depends on the request, caches must not serve one origin's answer to another.
  5. Return CORS headers only on endpoints called cross-origin. Not site-wide; the broader header set is on security headers.

What a WAF can do about CORS

Almost nothing. A CORS misconfiguration is in the response headers your server sends, and its effect happens inside the victim's browser when another site reads the response. The requests involved are ordinary requests with an Origin header.

Finding it is a matter of testing: send requests with unusual Origin values and read what comes back in Access-Control-Allow-Origin. The fix is a line of configuration in the application or the server in front of it.

Questions

What is CORS?

Cross-origin resource sharing is a set of HTTP headers that lets a server tell browsers which other origins may read its responses, relaxing the same-origin policy for chosen sites.

What does a CORS error mean?

The browser received a response but refused to give it to the page, because the server did not list the page's origin as allowed to read it. The fix is on the server that owns the resource.

What is a preflight request?

An OPTIONS request the browser sends before a cross-origin request with a non-simple method or custom headers, asking the server whether the real request is allowed.

Is Access-Control-Allow-Origin: * dangerous?

Not on public data that needs no sign-in. Browsers refuse to combine it with credentials. The danger is reflecting any origin, or a loosely matched one, together with credentials.

Does CORS protect against CSRF?

No. CORS controls whether a page can read a response, not whether a request is sent. CSRF needs tokens and SameSite cookies.