RestingOwl owl logo RestingOwl

CORS Explained:How Cross-Origin Requests Work

Quick Answer: CORS (Cross-Origin Resource Sharing) is a set of HTTP headers that lets your server tell the browser which other websites are allowed to read its responses. By default the browser's same-origin policy blocks a page on one site from reading data from another site. CORS is how a server safely opts in to sharing. The most important rule: never reflect any origin back with credentials enabled. Set an explicit allowlist of trusted origins, and only allow credentials for origins you fully trust.

CORS is one of the most misunderstood parts of web security. Developers meet it as an error in the browser console, reach for the quickest fix that makes the error disappear, and often turn a security control into a hole. This guide explains what CORS is actually doing, why the browser blocks cross-origin reads in the first place, and how to configure it so your API is reachable by the front ends you trust and no one else.

What Is the Same-Origin Policy?

To understand CORS you first need the rule it relaxes: the same-origin policy. An origin is the combination of scheme, host, and port, for example https://app.example.com. The same-origin policy says that JavaScript running on one origin cannot read responses from a different origin. This is a core browser protection. Without it, a malicious page you visit could quietly call your bank's API using your logged-in session and read your balance. The same-origin policy stops that by blocking the read.

The important detail is what it blocks. The browser may still send the request, but it hides the response from the calling script unless the target server says sharing is allowed. CORS is the mechanism the server uses to say exactly that.

How Does CORS Work?

When a page on origin A makes a cross-origin request to server B, the browser adds an Origin header naming A. Server B decides whether to allow it and answers with CORS response headers. If the server returns an Access-Control-Allow-Origin that matches the caller, the browser hands the response to the script. If it does not, the browser blocks the read and you see the familiar CORS error, even though the server may have processed the request.

Response HeaderWhat It Controls
Access-Control-Allow-OriginWhich origin is allowed to read the response. Either one exact origin or *.
Access-Control-Allow-MethodsWhich HTTP methods (GET, POST, PUT, DELETE) are permitted.
Access-Control-Allow-HeadersWhich custom request headers the client may send.
Access-Control-Allow-CredentialsWhether cookies and auth headers may be included. Cannot be used with *.
Access-Control-Max-AgeHow long the browser may cache the preflight result.

What Is a CORS Preflight Request?

For anything beyond a simple request, the browser sends a preflight first: an automatic OPTIONS request that asks the server for permission before sending the real one. A request is not simple if it uses a method like PUT or DELETE, sends custom headers, or uses certain content types. The preflight carries Access-Control-Request-Method and Access-Control-Request-Headers, and the server must answer with matching allow headers. Only if the preflight passes does the browser send the actual request. This is why you sometimes see two requests in the network tab for a single API call.

# Browser preflight (sent automatically)
OPTIONS /api/orders
Origin: https://app.example.com
Access-Control-Request-Method: DELETE

# Server response that allows it
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Credentials: true

What Are the Most Dangerous CORS Mistakes?

CORS misconfiguration is a real vulnerability class, not just a developer annoyance. The dangerous patterns all come from opening sharing too wide.

MistakeWhy It Is Dangerous
Reflecting the Origin header back with credentialsAny site becomes a trusted origin, so an attacker page can read a logged-in user's private data.
Using Access-Control-Allow-Origin: * on private APIsExposes responses to every website. Acceptable only for truly public data.
Allowing credentials with a wildcard-like configCombines an open origin policy with cookies, the exact recipe for data theft.
Trusting null as an originThe null origin can be forced by sandboxed frames and used to bypass your allowlist.

The worst of these is reflecting the caller's Origin back while also setting Access-Control-Allow-Credentials: true. Frameworks make this easy to do by accident when you try to support many front ends. It effectively means every website is allowed to make authenticated requests and read the results, which lets a malicious page steal data from any user who visits it while logged in to your app.

Does CORS Protect Against CSRF?

This is a common trap. CORS controls who can read a cross-origin response, but it does not stop the request from being sent. Cross-site request forgery abuses the fact that the browser sends cookies with requests the user never intended, and many CSRF attacks do not need to read the response at all: causing the state change is enough. So CORS is not a CSRF defence. You still need anti-CSRF tokens or the SameSite cookie attribute. The two controls solve different problems. For the full picture, read cross-site request forgery.

How Do You Configure CORS Safely?

Safe CORS Checklist
  1. 1Keep an explicit allowlist of trusted origins and compare the incoming Origin against it exactly.
  2. 2Never reflect an arbitrary Origin header back into Access-Control-Allow-Origin.
  3. 3Only set Access-Control-Allow-Credentials: true for origins you fully control and trust.
  4. 4Never combine credentials with a wildcard. Browsers forbid it, and workarounds recreate the risk.
  5. 5Use Access-Control-Allow-Origin: * only for genuinely public, non-credentialed data.
  6. 6Allow the minimum set of methods and headers your front end actually needs, not a blanket list.
  7. 7Do not treat the null origin as trusted.
  8. 8Remember CORS is not a CSRF defence: keep SameSite cookies and anti-CSRF tokens in place.

What Does OWASP Say About CORS?

OWASP treats overly permissive CORS as a security misconfiguration and covers it in the HTML5 Security Cheat Sheet and the ASVS configuration controls. The guidance is to validate the Origin against a strict allowlist rather than reflecting it, to avoid the wildcard on any endpoint that returns private or credentialed data, and to never pair credentials with an open origin policy. OWASP also stresses that CORS complements, but never replaces, server-side authorisation: every request must still be authenticated and authorised on its own, because a determined client can send requests regardless of CORS.

Related reading: CORS sits alongside your other browser-facing controls. See CSRF protection, Content Security Policy, and secure session cookies for the complete picture of cross-origin and browser security.

References

  1. 1MDN: Cross-Origin Resource Sharing (CORS)
  2. 2OWASP: HTML5 Security Cheat Sheet (CORS)
  3. 3W3C / WHATWG Fetch Standard: CORS protocol

Q&A Section

CORS, or Cross-Origin Resource Sharing, is a set of HTTP headers a server uses to tell the browser which other websites are allowed to read its responses. By default the browser blocks a page on one site from reading data from another site, a rule called the same-origin policy. CORS is how a server safely says which specific origins are allowed to make an exception.
A CORS error means the browser blocked your JavaScript from reading a cross-origin response because the server did not return an Access-Control-Allow-Origin header that matches your page's origin. The server may still have processed the request. The fix is to configure the server to allow your exact origin, not to disable the check or reflect every origin, which would create a security hole.
The wildcard is fine for genuinely public data that carries no cookies or credentials, such as an open read-only API. It is dangerous on any endpoint that returns private or user-specific data, because it lets every website read the response. You also cannot combine the wildcard with credentials, so if your API uses cookies you must specify exact trusted origins instead.
No. CORS controls who can read a cross-origin response, but it does not stop the request from being sent, and many CSRF attacks only need to trigger a state change, not read the reply. You still need dedicated CSRF defences such as anti-CSRF tokens and the SameSite cookie attribute. CORS and CSRF protection solve different problems and should both be in place.
A preflight is an automatic OPTIONS request the browser sends before certain cross-origin requests to ask the server for permission. It happens for requests that use methods like PUT or DELETE, custom headers, or non-simple content types. The server must answer with matching Access-Control-Allow-Methods and Allow-Headers. Only if the preflight passes does the browser send the real request, which is why you sometimes see two requests for one call.
Copied!