What is CORS? How browser access works
Author: Domeny.ForSale editorial team Published
In brief: CORS (Cross-Origin Resource Sharing) is a mechanism that lets a server allow a browser to make a response available to a script running on a different origin. The server’s response headers specify permission, and the browser enforces it. A CORS error therefore usually means a problem with permission to read the response, not necessarily a failure to connect to the server.
Why browsers restrict cross-origin access
Browsers enforce the same-origin policy. This limits a script’s ability to read data from another origin. It prevents a page open in one tab from freely fetching and reading private data from other services where the user is logged in.
An origin consists of three elements: the protocol, hostname and port. Changing even one of them means a different origin. The path after the hostname does not matter here.
For a page at https://shop.example, the comparison looks like this:
| Destination address | Same origin? | Reason |
|---|---|---|
https://shop.example/products | Yes | Only the path changes |
https://api.shop.example | No | Different hostname |
http://shop.example | No | Different protocol |
https://sklep.example:8443 | No | Different port from the default of 443 |
CORS lets a server relax this policy in a controlled way. It is not an HTTP method like GET or POST, but a mechanism based on HTTP headers.
A simple example: a shop fetches a product list
Suppose a website runs at https://shop.example, while its API—the interface that provides the application’s data—runs at https://api.shop.example. The page’s script wants to fetch products:
fetch('https://api.sklep.example/produkty')
.then(response => response.json())
.then(products => console.log(products));
Because the addresses have different origins, the browser adds a header to the request identifying the page’s origin:
Origin: https://sklep.example
The API server can respond with data and a header granting permission:
Access-Control-Allow-Origin: https://sklep.example
The browser compares this value with the page’s origin. If the permission matches, it makes the response available to the script. If the header is missing or specifies a different address, the script cannot read the response—even if the server returned a 200 OK status.
The permission header must come from the API server. Adding Access-Control-Allow-Origin to the browser-side request code does not solve the problem.
When the browser sends OPTIONS first
Not every cross-origin request is sent immediately. For some requests, the browser first performs a preflight, a preliminary request that checks the server’s permission.
For example, submitting an order using POST with the content type application/json triggers this step. Using the Authorization header does too. A preflight request might look like this:
OPTIONS /orders HTTP/1.1
Origin: https://sklep.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
The server should respond with permission covering the origin, method and required headers:
Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
Only after a successful check does the browser send the actual POST request. The response to that POST request must also contain the appropriate Access-Control-Allow-Origin header.
Not every POST request requires a preflight. The headers and content type also matter; a basic GET request without additional headers often does not need a preflight.
Cookies and the wildcard: two important pitfalls
A public API whose responses should be available to scripts from any origin can return:
Access-Control-Allow-Origin: *
However, the wildcard is not suitable for requests sent with cookies in a mode that includes credentials. In this case, the code may use credentials: 'include', and the server must specify a particular allowed origin and return:
Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Credentials: true
CORS configuration alone does not guarantee that cookies will be sent. Cookie rules, including SameSite, and the browser’s privacy restrictions also apply.
If the server supports several trusted origins, it should check the Origin header against an allowlist. Blindly reflecting any received origin while allowing credentials can expose private responses. When a response depends on Origin, also include Vary: Origin so that caches can distinguish between response variants.
How to diagnose a CORS error
Open the Network tab in your browser’s developer tools and perform these four checks:
- Compare the protocol, host and port of the page and the API.
- Check whether there is an
OPTIONSrequest and whether the server responds to it correctly. - Inspect the response headers of the actual request, including when the server returns an error.
- Check whether permission covers the method, headers and any credentials being used.
A request working in curl does not prove that CORS is configured correctly: this tool does not enforce browser restrictions. Setting mode: 'no-cors' will not unlock access to JSON either—it produces an opaque response whose body is unavailable to the script.
CORS does not replace login, permission checks or protection against CSRF, where another site triggers unwanted actions. Some requests can reach the server even when reading the response is blocked. Start fixing the problem in the configuration of the API or a proxy you control, rather than by disabling browser security.
See also: What is an API?
