Allow Multiple Specific Origins

Handle CORS when multiple frontend domains need access. Learn the server-side pattern for dynamically setting Allow-Origin from an allowlist.

Basic CORS

Detailed Explanation

Dynamic Origin from an Allowlist

The Access-Control-Allow-Origin header accepts only one value (or *). You cannot comma-separate multiple origins. When you need to allow https://app.example.com and https://admin.example.com, the server must dynamically check the incoming Origin header against an allowlist and echo it back if it matches.

Server Logic (Pseudocode)

allowed = ["https://app.example.com", "https://admin.example.com"]
if request.headers["Origin"] in allowed:
    response.headers["Access-Control-Allow-Origin"] = request.headers["Origin"]
    response.headers["Vary"] = "Origin"

Nginx Implementation

map $http_origin $cors_origin {
    default "";
    "https://app.example.com"   "$http_origin";
    "https://admin.example.com" "$http_origin";
}

if ($cors_origin != "") {
    add_header Access-Control-Allow-Origin $cors_origin always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
    add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
    add_header Access-Control-Allow-Credentials "true" always;
    add_header Vary Origin always;
}

Express.js Implementation

const allowedOrigins = [
  "https://app.example.com",
  "https://admin.example.com",
];

app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error("Not allowed by CORS"));
    }
  },
  credentials: true,
}));

Why Vary: Origin Is Critical

Because the response changes depending on which origin sent the request, caches must know to key on the Origin header. Without Vary: Origin, a CDN could cache the response for app.example.com and serve it to admin.example.com, which would fail the browser's origin check.

Use Case

A company with separate frontend apps for customers (app.example.com) and internal staff (admin.example.com), both consuming the same REST API. Each origin needs to be explicitly whitelisted.

Try It — CORS Header Builder

Open full tool