How to Enable Access-Control-Allow-Origin for Multiple Domains

Reynaldi
Reynaldi •

In this article, you will learn how to enable Access-Control-Allow-Origin for multiple domains. The header only accepts a single value — you can’t pass it a comma-separated list of domains — so you need to dynamically set it server-side.

Dynamically Set the Origin

The Access-Control-Allow-Origin header doesn’t support multiple values. So instead of hardcoding a domain, you check the incoming request’s Origin against a whitelist and reflect it back.

Here’s the pattern in the most common setups:

Node.js / Express

const allowedOrigins = [
'https://app.example.com',
'https://dashboard.example.com',
'https://staging.example.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();
});

Python / FastAPI

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
allowed_origins = [
"https://app.example.com",
"https://dashboard.example.com",
"https://staging.example.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_methods=["*"],
allow_headers=["*"],
)

Next.js API Routes

const allowedOrigins = [
'https://app.example.com',
'https://dashboard.example.com',
'https://staging.example.com',
];
export async function GET(request) {
const origin = request.headers.get('origin');
const headers = new Headers();
if (allowedOrigins.includes(origin)) {
headers.set('Access-Control-Allow-Origin', origin);
headers.set('Vary', 'Origin');
}
return new Response(JSON.stringify({ data: 'hello' }), { headers });
}

The logic is the same in every case: look at the Origin header, check if it’s allowed, and echo it back. If it’s not on the list, you don’t set the header at all, and the browser blocks the request.

Why You Can’t Just List Multiple Domains

If you’ve tried something like this:

Access-Control-Allow-Origin: https://app.example.com, https://dashboard.example.com

…you’ve already seen it doesn’t work. The CORS specification defines Access-Control-Allow-Origin as accepting exactly one of three values:

  • A single origin (e.g., https://app.example.com)
  • The wildcard *
  • The literal string null

That’s it. No comma-separated lists, no space-separated lists, no patterns. If the browser sees anything that doesn’t match one of those forms, it treats the CORS check as failed and blocks the response.

And while * technically allows every domain, it comes with a big catch: you can’t use * when the request includes credentials (cookies, auth headers). The browser will flat-out reject it. So for any authenticated API, the wildcard is off the table, and you’re back to the dynamic whitelist approach.

How the Dynamic Whitelist Approach Works

Here’s what’s actually happening under the hood when a cross-origin request hits your server:

  1. The browser sends the request with an Origin header — e.g., Origin: https://app.example.com. The browser adds this automatically; the frontend code doesn’t set it.

  2. Your server checks the whitelist. If the origin is on the list, the server sets Access-Control-Allow-Origin to that exact origin. If not, it either omits the header or returns a default.

  3. The browser compares. It looks at the Access-Control-Allow-Origin value in the response. If it matches the requesting origin, the response goes through. If not, the browser blocks it.

Two things to get right here:

Always set Vary: Origin. This tells caches (CDNs, proxies, browser cache) that the response changes based on the Origin header. Without it, a CDN might cache the response for app.example.com and serve it to a request from dashboard.example.com — which the browser will block because the origins don’t match.

Handle preflight requests. For non-simple requests (PUT, DELETE, custom headers, etc.), the browser sends an OPTIONS request first. Your server needs to respond to that OPTIONS request with the same CORS headers — Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers — and a 204 No Content status. If the preflight fails, the actual request never fires.

Enabling Access-Control-Allow-Origin on a Domain You Don’t Control

If you’re calling a third-party API that doesn’t include your origin in its CORS headers, no amount of frontend configuration can fix that. The header has to come from the server serving the response.

The workaround is a CORS proxy — a server that forwards your request to the API server-to-server (where CORS doesn’t apply) and returns the response with the right headers. Corsfix does exactly this:

// Before — blocked by CORS
fetch('https://api.thirdparty.com/data')
// After — proxied through Corsfix
fetch('https://proxy.corsfix.com/?https://api.thirdparty.com/data')

No server setup, no CORS configuration. Corsfix adds the appropriate Access-Control-Allow-Origin header so your frontend can read the response.

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

Conclusion

Access-Control-Allow-Origin only accepts a single origin or *. If you control the server, dynamically check the incoming Origin against a whitelist and reflect it back. Always set Vary: Origin. If you don’t control the server, use a proxy like Corsfix to handle the headers for you.

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.