GuidesUNDERSTAND

How cross-origin resource sharing works

Browsers restrict client-side scripts from reading responses sent by a different origin. Cross-origin resource sharing defines how servers tell the browser to loosen that boundary.

Origins and the same-origin policy

An origin consists of three parts: scheme, host, and port. Two URLs belong to different origins if any part of that tuple differs, which means api.example.com and example.com are distinct origins to the browser. Under the same-origin policy, browser runtime environments isolate these boundaries and block scripts from reading cross-origin responses by default. Cross-origin resource sharing provides a standardized relaxation mechanism, serving as a read-permission check that the browser evaluates after the server response arrives.

What the browser checks during a request

When a web page on origin A initiates a fetch to origin B, the browser appends an Origin request header identifying origin A. The target server processes the request and sends its response headers back. The browser checks for an Access-Control-Allow-Origin header matching origin A or containing a wildcard. A match allows JavaScript to access the payload, while a missing or mismatched header causes the browser to withhold the response.

Requests fall into simple and non-simple categories. The browser sends simple requests directly, whereas non-simple requests trigger an automatic OPTIONS preflight call before the actual request leaves the browser. A request qualifies as simple when it satisfies all of these constraints:

  • The HTTP method is GET, HEAD, or POST.
  • Request headers contain only safelisted headers such as Accept, Accept-Language, or Content-Language.
  • The Content-Type header uses application/x-www-form-urlencoded, multipart/form-data, or text/plain.
  • No event listeners are registered on any XMLHttpRequestUpload object used in the request.
  • The request body is not a ReadableStream.

The server never sees the error

Cross-origin failures occur entirely inside the browser runtime. The upstream server receives the request, completes processing, and returns an HTTP 200 status code normally. Server logs record a successful transaction, and debugging utilities like curl or Postman display the payload without friction. Reading the Postman comparison clarifies why non-browser clients bypass this enforcement.

Where the fix belongs

CORS configuration belongs on the server answering the request. If you control the API, add the appropriate Access-Control-Allow-Origin and Access-Control-Allow-Methods headers to your server responses. When calling a third-party API that does not support browser clients, you must route requests through an intermediary backend that you control, or use a proxy that attaches permissive headers. Disabling browser security flags is an environment workaround that cannot solve the problem for production users.

Public proxies like cors.dev solve this for public data by fetching the upstream resource and returning it with cross-origin headers enabled. Because the proxy operator can observe traffic routed through the service, this pattern applies exclusively to public endpoints. Authenticated endpoints with private credentials must use your own dedicated backend service instead.

Good questions.

Is CORS a security feature for my API?

No. CORS protects browser users from malicious web applications reading private data across origins. It does not protect an API from direct scrapers, curl commands, or mobile clients, which ignore browser security policies completely.

Does CORS apply between two of my own subdomains?

Yes. The browser treats app.example.com and api.example.com as distinct origins because their hostnames differ. Cross-origin calls between subdomains require explicit CORS response headers.

What is the difference between CORS and CSP?

CORS governs cross-origin read permissions granted by external servers to your frontend. Content Security Policy (CSP) is a set of rules your own site declares to restrict which domains your browser page can load scripts, styles, or network connections from.