CORS explained: what it is and how it gets misconfigured
CORS is not a firewall. It controls which websites may read your API's responses in a browser. Learn it properly and avoid the risky settings.

Browsers apply the same-origin policy: a page from a.example cannot read responses from b.example unless b.example allows it. An origin is the combination of scheme, host and port.
CORS (Cross-Origin Resource Sharing) is how a server tells the browser which other origins may read its responses.
What CORS does and does not do
- It restricts what a browser lets a web page read. It is enforced by the browser only.
- It does not stop requests from curl, scripts or servers, and it does not protect an API from being called. Authentication does that.
- A cross-site request can still be sent (and may change data). CORS only blocks the page from reading the answer (CSRF is a separate problem).
The key headers
Access-Control-Allow-Origin: which origin may read the response (one origin, or*).Access-Control-Allow-Credentials: true: allow cookies and auth to be sent and the response read.Access-Control-Allow-Methods/Allow-Headers: what a cross-origin request may use.Access-Control-Max-Age: how long to cache the preflight answer.
Preflight
For "non-simple" requests (such as PUT, DELETE, or JSON with custom headers) the browser first sends an OPTIONS preflight asking if the real request is allowed. The server answers with the allowed origin, methods and headers.
Dangerous misconfigurations
1. Reflecting any Origin with credentials. The server copies whatever Origin the request has into Access-Control-Allow-Origin and also sends Allow-Credentials: true. Any malicious site can now read a logged-in user's private data. The browser forbids * with credentials, so developers "fix" it this way, which is worse.
2. Weak origin checks. Using a substring or regex such as "ends with example.com" matches evilexample.com, or trusting null origin (which sandboxed pages can send).
3. Allowing all subdomains when one of them has an XSS or a takeover bug.
4. Wildcard on private APIs. * is fine for truly public data, but not for responses that depend on cookies or private tokens.
5. Trusting internal origins such as http://localhost in production.
Safe approach
- Keep an exact allow-list of origins (full scheme, host, port). Compare exactly, not by pattern.
- Use
*only for public, non-credentialed resources. - Send
Vary: Originwhen the response differs by origin, so caches do not mix them up. - Do not enable credentials unless you must.
- Limit allowed methods and headers.
- Remember: authentication and authorization on the server still do the real protection.
How to test (on your own app)
Send a request with a made-up Origin header and see if it is reflected back, with credentials allowed. If yes, fix it.
Takeaway: CORS only decides who can read responses in a browser. Use an exact origin allow-list, avoid reflecting origins with credentials, and keep real access control on the server.
Web Security series, Part 2 (more bug classes and hardening): 2 of 6. Previous: JWT security: common mistakes and how to avoid them.
