The first call to an API hosted elsewhere almost always ends the same way: the console shows a blocking message, the network tab shows a response that did arrive, and the code cannot touch it.
This mechanism is neither a bug nor pointless strictness. It stops any random page from reading, with your cookies, the data of a site you are signed into.
Definition
CORS, short for cross-origin resource sharing, is the protocol through which a server allows a page from another origin to read its responses. The permission travels in HTTP headers, and the browser is what enforces it.
Nothing written on the page side unblocks it: a refused fetch() call is fixed on the server being called, or not at all.
What an origin is
An origin is the trio of protocol, domain and port. A single difference is enough to change origin, which the table shows across three columns for a page served from https://app.example.com.
| URL being called | Same origin | What differs |
|---|---|---|
https://app.example.com/api | Yes | Nothing, only the path changes |
https://api.example.com | No | The subdomain |
http://app.example.com | No | The protocol |
https://app.example.com:8443 | No | The port |
That second row surprises people often: a subdomain of the same site is still another origin, and therefore needs explicit permission.
The preflight request
Some requests go out directly. Others are preceded by an automatic OPTIONS call that asks for permission before sending anything. That detour kicks in as soon as a request steps outside the simplest frame: a method other than GET or POST, a custom header, or a body announced as application/json.
// On the server, the response has to carry these headers
function allow(response, origin) {
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
response.setHeader("Access-Control-Max-Age", "86400");
}The last header caches the permission, which spares an extra round trip before every call. Each browser applies its own ceiling, often far below the value you ask for.
The block protects the reading of the response, not the server. A simple request really does reach its destination before being blocked at the reading stage: a write therefore still needs protection on the server, independently of this mechanism.
Frequently asked questions
Why does the same call work from a REST client?
Because the rule is enforced by the browser, and by the browser alone. Neither a client such as Postman, nor a curl command, nor a Node.js script pays any attention to these headers. A call that answers fine from the command line and fails in the tab is therefore perfectly normal.
Can the block be worked around from the page?
No, and that is by design. The mode: "no-cors" option lets the request go out but returns an opaque response whose body and status are both unreadable. The only lasting solution is to go through your own server, which calls the API and hands you the result from your own origin.
Why does the star not work with cookies?
Because the * value is incompatible with a call that carries credentials. You then have to echo the caller's exact origin in Access-Control-Allow-Origin, add Access-Control-Allow-Credentials set to true, and set credentials to include on the page side. That setup is built step by step in the Node.js course.