All articles

CORS: why the browser blocks "normal-looking" requests to another domain

A common footgun with Express: the popular cors npm package, when configured with no origin option or with origin: true, reflects back whatever Origin header the request sent as Access-Control-Allow-Origin. That effectively allows every origin while looking like a locked-down config — it works fine until someone adds credentials: true and realizes any site can now make authenticated requests on a logged-in user's behalf.

The Same-Origin Policy

By default, browsers enforce the Same-Origin Policy: JavaScript running on a page from one origin (domain + scheme + port) can't read responses from requests made to a different origin. This is a fundamental security mechanism that stops a malicious script on one site from stealing data from another site where the user happens to be logged in.

How CORS relaxes that policy

CORS (Cross-Origin Resource Sharing) is a set of HTTP headers a server uses to explicitly allow certain (or all) outside origins to read its responses. The key header is Access-Control-Allow-Origin, which specifies which domains are permitted.

The wildcard-plus-credentials trap

When a request is sent with credentials: 'include' — needed to send a session cookie to an API on a different subdomain — browsers flatly refuse to accept Access-Control-Allow-Origin: * in the response. The spec explicitly forbids combining a wildcard origin with credentials; the server must echo back the exact requesting origin instead.

Why you'd need this

  • Diagnosing a CORS error and figuring out exactly which headers the server is missing.
  • Catching an overly permissive cors middleware config before it ships to production.
  • Understanding the difference between "simple" requests and preflight (OPTIONS) requests.

CORS doesn't protect the server from attacks

CORS is a mechanism that only restricts browsers and only JavaScript code running on a web page; it does absolutely nothing to stop an attacker from sending the same request directly via curl, Postman, or a backend script, bypassing the browser entirely. That's why CORS should never be treated as a substitute for real server-side authorization — it's a tool for controlling which sites can use an API from a user's browser, not a barrier against attacks in general.

Try the tool