CORS: Credentials, Server Config & Debugging
Cookies & Credentials
// Client -- must opt in to sending cookies cross-origin
fetch('https://api.example.com/me', { credentials: 'include' });# Server -- MUST pair with a specific origin, NEVER a wildcard.
# credentials + wildcard is explicitly disallowed by the spec --
# would let literally any site make authenticated requests as the user.
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
# Reading a custom response header from JS requires explicit exposure --
# by default only a small safelisted set is readable
Access-Control-Expose-Headers: X-Total-CountServer-Side Configuration (Express example)
const cors = require('cors');
const allowedOrigins = ['https://app.example.com', 'https://staging.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,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
}));
// Middleware centralizes this instead of manually setting headers
// on every single route handler, and auto-handles preflight OPTIONS.Local Dev: Proxy Instead of CORS Config
// vite.config.js -- frontend dev server forwards /api/* to the
// real backend, so the browser only ever sees same-origin requests
// during local dev -- no CORS headers needed just for development
export default {
server: {
proxy: {
'/api': 'http://localhost:4000',
},
},
};Mental Model & Security Scope
Server DECLARES what it allows (via headers); browser ENFORCES it -- the fix for a legitimate blocked request is almost always server-side config, not client code.
A CORS failure is opaque by design -- the requesting JS gets a generic network error, not the actual response status/body, making the network tab/server logs the more useful debugging tool.
CORS only constrains browser-page JavaScript -- curl, Postman, and server-to-server calls ignore it entirely. It is NOT a substitute for real authentication/authorization on sensitive endpoints.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free