Home / Software y Cloud / CORS: the invisible gatekeeper that decides which websites can talk to each other

CORS: the invisible gatekeeper that decides which websites can talk to each other

When you open a web page, your browser does not just render HTML: it runs JavaScript that requests data from other servers, from analytics to recommendations. Each of those requests crosses an invisible boundary that few people notice, yet it decides a large part of the security of everything you do online. Its name is CORS, and in this article you will understand what it is, how it is negotiated and why blocking and allowing requests is not as simple as it seems.

The rule that protects your data: the same origin

Everything starts with the same-origin policy, the principle that a page can only read data from its own origin. An origin is the combination of protocol (https), domain (mydomain.com) and port (:443). https://app.mydomain.com and https://mydomain.com are different origins even though they share a domain, because the subdomain changes the equation. Without this rule, any website you visited could read your open email in another tab or steal your cookies.

The same-origin policy dates back to the 1990s and was designed to stop a script on one page from accessing the content of another. It is a browser-side measure: it blocks the reading of the response, not always the sending of the request. That distinction is key, because CORS is built on top of it.

The problem: when two sites must talk to each other

The modern web needs different origins to cooperate. A store that charges through a payment gateway, an application that loads data from a third-party API, or a front-end that consumes its own backend on another domain all depend on cross-origin requests. If the same-origin policy blocked everything, these applications simply would not work.

This is where CORS (Cross-Origin Resource Sharing) comes in, an HTTP mechanism by which a server explicitly declares which origins are allowed to access its resources. The server grants permission; the browser only enforces it.

How CORS is negotiated: headers and requests

The negotiation happens through HTTP headers. The browser adds an Origin header to the request indicating which origin it comes from, and the server replies with Access-Control-Allow-Origin (ACAO) stating which origins may read it. If the request origin is not on the allowed list, the browser discards the response: the request reached the server, but the JavaScript on your page never sees the result. The block is, again, a browser decision, not a server one.

Requests fall into two types. Simple requests, such as a GET or a POST with basic content types, are sent directly and only need the ACAO header in the response. The rest, such as a request with Authorization or a PUT with Content-Type: application/json, trigger a preflight request: before the real one, the browser sends an OPTIONS request asking the server which methods and headers it allows. The server answers with Access-Control-Allow-Methods and Access-Control-Allow-Headers, and only if it approves does the browser launch the actual request.

Credentials (session cookies, authentication) add another layer. To send them, the request must be marked with credentials and the server must respond with Access-Control-Allow-Credentials: true. In that case, the * wildcard in ACAO is forbidden: you must list the exact origin, because combining credentials with an open permission would be a door left open to any site.

What CORS is not: a boundary, not a firewall

It is worth being clear about what it protects and what it does not. CORS does not stop an attacker from sending requests to a server from their own machine; it only decides whether the victim’s browser exposes the response to a script from another website. Nor does it replace authentication or CSRF (Cross-Site Request Forgery) protection, an attack that tricks the browser into performing actions with your credentials. Misconfiguring CORS (for example, reflecting the origin without validating it, or using a wildcard with credentials) can turn a private resource into something anyone can read.

A common and mistaken practice is to reply with the Origin value received. If an attacker sends Origin: https://evil.com, the server reflects it and the browser accepts it. The correct approach is to keep an allowlist of known origins on the server and only return the ones you really trust.

The result: a web that talks, but with permissions

At its core, CORS is a permission system negotiated through headers: the server declares who is allowed to read, the browser enforces the rule, and the preflight stops risky requests from running without consent. It is not a wall but an access protocol with keys that are handed out and revoked.

Next time a developer console shows you a CORS error, you will know what is going on behind the scenes: your page asked another origin for data, the server did not recognize the source origin and the browser, faithful to its policy, refused to hand over the response. It is not a code bug: it is a guard doing its job.