Single Origin with Credentials
Configure CORS to allow one specific origin with credentials support. Understand the interplay between Allow-Origin, Allow-Credentials, and the Vary header.
Detailed Explanation
Specific Origin + Credentials
When your frontend and API live on different subdomains and the API uses cookies or Authorization headers, you need a targeted CORS policy that names the exact origin and enables credentials.
Generated Headers
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600
Vary: Origin
Why Not Wildcard?
Browsers enforce a strict rule: if Access-Control-Allow-Credentials is true, the Allow-Origin header must be the exact requesting origin, not *. Violating this rule causes the browser to block the response without any visible server-side error — a silent failure that is notoriously difficult to debug.
The Vary Header
When your server dynamically sets Access-Control-Allow-Origin based on the incoming Origin request header, you must include Vary: Origin in the response. This tells CDNs and browser caches to store separate cached versions per origin, preventing one origin's CORS response from being served to a different origin.
Express.js Example
const cors = require("cors");
app.use(cors({
origin: "https://app.example.com",
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
maxAge: 3600,
}));
Cookie Considerations
For cookies to be sent cross-origin, three conditions must all be true: the server sends Allow-Credentials: true, the client sets fetch({ credentials: "include" }) (or withCredentials: true for XHR), and the cookie's SameSite attribute is set to None with Secure.
Use Case
A SaaS application where the dashboard at app.example.com talks to the API at api.example.com. Session cookies need to travel cross-origin for authentication, so credentials must be enabled with the exact frontend origin.