How Does Access-Control-Allow-Origin Work?

Reynaldi
Reynaldi •

This article explains how the Access-Control-Allow-Origin header works and why browsers need it to allow cross-origin requests. It covers the mechanics step by step, common pitfalls, and what to do when you don’t control the server.

If you’re here because you’re hitting a CORS error and need a fix now, skip to What If You Don’t Control the Server?

The Short Answer

Access-Control-Allow-Origin is a response header that a server sends back to tell the browser: “this origin is allowed to read my response.”

When your JavaScript at https://myapp.com makes a request to https://api.example.com, the browser checks the response for this header. If it matches your origin, you get the data. If it’s missing or doesn’t match, the browser blocks the response and you get a CORS error.

The header accepts one of three values:

Access-Control-Allow-Origin: https://myapp.com

Allows only https://myapp.com.

Access-Control-Allow-Origin: *

The wildcard — allows any origin.

Access-Control-Allow-Origin: null

Matches the null origin. You almost never want this (more on that later).

Why This Header Exists

Browsers enforce the Same-Origin Policy: JavaScript on one origin cannot read responses from a different origin. Two URLs share an origin only if the protocol, domain, and port all match.

This exists for security. Without it, any site you visit could silently make requests to your bank or email and read the responses.

But legitimate cross-origin requests happen all the time — your frontend calling your API on a different subdomain, fetching data from a third-party service, etc. CORS (Cross-Origin Resource Sharing) is the mechanism that lets servers opt in to sharing responses with other origins, and Access-Control-Allow-Origin is the core header that makes it work.

How the Browser Enforces It (Step by Step)

Step 1: Your code makes a request.

fetch("https://api.example.com/data")

Step 2: The browser adds an Origin header automatically:

GET /data HTTP/1.1
Host: api.example.com
Origin: https://myapp.com

Step 3: The server responds with Access-Control-Allow-Origin:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Content-Type: application/json

Step 4: The browser compares the two. If the value matches the requesting origin (or is *), JavaScript gets the response. Otherwise, the browser blocks it.

The key insight: the server still receives and processes the request. CORS is entirely browser-side enforcement. Your server returned a 200 — the browser just refused to let your JavaScript see it.

A note on preflight: If your request uses methods like PUT or DELETE, includes custom headers, or sends application/json, the browser first sends an OPTIONS request to ask permission. The server needs to return Access-Control-Allow-Origin on that preflight response too. The header works the same way — but if you’re seeing CORS errors on requests that seem correct, check whether the preflight is being handled.

Common Pitfalls and Edge Cases

You can’t list multiple origins. The header accepts a single value — one origin or *. If you need to allow multiple origins, check the incoming Origin against a whitelist and echo it back dynamically:

// Express example
const allowedOrigins = ["https://myapp.com", "https://otherapp.com"];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Vary", "Origin");
}
next();
});

The Vary: Origin header tells caches that the response differs by origin. Without it, a cached response for one origin could be served to another.

The wildcard * doesn’t work with credentials. If your request includes cookies or auth (credentials: "include"), the server must respond with the specific origin, not *. It also needs to include Access-Control-Allow-Credentials: true.

Forgetting to handle OPTIONS. Preflight requests use the OPTIONS method. If your server doesn’t handle it, it returns a 404 or 405 and the preflight fails before your actual request is ever sent.

The null origin. Requests from file:// pages, sandboxed iframes, and certain redirects send Origin: null. Don’t whitelist it — any page can sandbox an iframe to produce a null origin, making it effectively the same as allowing everyone.

What If You Don’t Control the Server?

This is one of the most common CORS frustrations. The API works fine in curl or Postman (which don’t enforce CORS) but fails in the browser because the response is missing the header. Since it’s not your server, you can’t add it.

Build your own proxy. Route requests through your backend — server-to-server requests aren’t subject to CORS. This works but means hosting and maintaining a server just to forward requests.

Use a managed CORS proxy. A service like Corsfix handles this for you — it proxies the request server-side and adds the correct CORS headers, so you don’t have to build and maintain your own proxy.

// instead of
fetch("https://api.example.com/data");
// use Corsfix
fetch("https://proxy.corsfix.com/?" + "https://api.example.com/data")
.then((response) => response.json())
.then((data) => console.log(data));

For local development, this works instantly without registration. For live websites, set up your domain (takes 30 seconds).

Browser extensions and --disable-web-security are not solutions. They only affect your browser. Your users will still get CORS errors.

Conclusion

Access-Control-Allow-Origin is a server response header that tells the browser which origins can read the response. It accepts a specific origin, *, or null — and you’ll usually want either a specific origin or a dynamic whitelist check.

CORS is browser-side enforcement — the server processes the request regardless. If you’re hitting CORS errors on an API you don’t control, a server-side proxy is the reliable fix, whether you build your own or use a managed service like Corsfix.

Corsfix is free to get started, and you only need to upgrade when you go to production.

It's time to build great websites without CORS errors

Try our CORS proxy for free, all features included.

Fix CORS errorsNo credit card required.