CORS
02 / 02

Credentials, Server Config & Debugging

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-Count

Server-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