Web Development
Access-Control-Allow-Origin
HTTP Headers
Cross-Origin Resource Sharing
Web Security

How does the 'Access-Control-Allow-Origin' header work?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Access-Control-Allow-Origin is a CORS response header that tells browsers which origins are allowed to read a cross-origin response. It is not an authentication mechanism, and it does not control whether the HTTP request reaches the server at all.

That distinction matters. The server may receive and process a request, yet the browser can still block JavaScript from reading the response because the CORS policy did not allow that origin.

Same-Origin Policy and What CORS Changes

Browsers enforce the same-origin policy for scripts. Under that policy, a page loaded from one origin cannot freely read responses from another origin unless the target server opts in.

An origin is the combination of:

  • scheme
  • host
  • port

If any of those differ, the request is cross-origin from the browser’s point of view.

Access-Control-Allow-Origin is one of the headers the server uses to opt in to that cross-origin read access.

The Simple Case

For a simple cross-origin request, the browser sends the request with an Origin header. The server then returns a matching CORS response header.

http
1HTTP/1.1 200 OK
2Access-Control-Allow-Origin: https://app.example.com
3Vary: Origin
4Content-Type: application/json

If the browser sees an allowed origin, JavaScript may read the response. If the header is missing or mismatched, the browser blocks script access even though the network request itself succeeded.

That is why CORS errors can be confusing: the server logs may show a successful request while the frontend still reports a blocked response.

Preflight Requests

If the request is not “simple” enough, the browser sends a preflight OPTIONS request first. This asks the server whether the real request method and headers are allowed.

A typical preflight response looks like this:

http
1HTTP/1.1 204 No Content
2Access-Control-Allow-Origin: https://app.example.com
3Access-Control-Allow-Methods: GET, POST, PUT
4Access-Control-Allow-Headers: Authorization, Content-Type
5Access-Control-Max-Age: 600
6Vary: Origin

If the preflight response is not acceptable, the browser does not send the actual application request.

Credentials Change the Rules

If the browser sends credentials such as cookies or HTTP authentication, you cannot use Access-Control-Allow-Origin: *.

Instead, the server must return the exact trusted origin and also include:

http
Access-Control-Allow-Credentials: true

The client side must also opt in, for example with fetch(..., { credentials: "include" }).

This is one of the most common CORS mistakes: combining credentials with a wildcard origin and expecting browsers to allow it.

A Safe Server Pattern

A typical safe pattern is to maintain an allowlist and echo back only trusted origins.

javascript
1import express from "express";
2
3const app = express();
4const allowed = new Set([
5  "https://app.example.com",
6  "https://admin.example.com",
7]);
8
9app.use((req, res, next) => {
10  const origin = req.headers.origin;
11
12  if (origin && allowed.has(origin)) {
13    res.setHeader("Access-Control-Allow-Origin", origin);
14    res.setHeader("Vary", "Origin");
15    res.setHeader("Access-Control-Allow-Credentials", "true");
16  }
17
18  if (req.method === "OPTIONS") {
19    res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
20    res.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization");
21    return res.status(204).end();
22  }
23
24  next();
25});

The Vary: Origin header is important when caches are involved, because the response can change depending on the request origin.

Common Pitfalls

A common mistake is treating CORS as if it were API authentication. It is not. It is a browser-enforced read policy.

Another issue is returning * while also expecting browsers to send cookies. Credentialed requests require an explicit origin, not a wildcard.

Developers also often configure the main request headers but forget the OPTIONS preflight path, which breaks anything beyond the simplest request types.

Finally, reflecting arbitrary origins without an allowlist creates a weak and often accidental policy. Dynamic reflection should still be constrained.

Summary

  • 'Access-Control-Allow-Origin tells browsers which origins may read a cross-origin response.'
  • It does not replace authentication and does not stop the server from receiving the request.
  • Non-simple requests may require a preflight OPTIONS exchange.
  • Credentialed requests require an explicit origin, not *.
  • Safe CORS setups usually use an allowlist and include Vary: Origin when the value is dynamic.

Course illustration
Course illustration

All Rights Reserved.