How to Enable Access-Control-Allow-Origin for Multiple Domains
On This Page
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, Requestfrom 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:
-
The browser sends the request with an
Originheader — e.g.,Origin: https://app.example.com. The browser adds this automatically; the frontend code doesn’t set it. -
Your server checks the whitelist. If the origin is on the list, the server sets
Access-Control-Allow-Originto that exact origin. If not, it either omits the header or returns a default. -
The browser compares. It looks at the
Access-Control-Allow-Originvalue 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 CORSfetch('https://api.thirdparty.com/data')
// After — proxied through Corsfixfetch('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.