Back to Blog
CORSSecurityAPIWeb Development

CORS Explained: Cross-Origin Resource Sharing

Understand and configure CORS properly. Learn preflight requests, headers, and common issues.

B
Bootspring Team
Engineering
February 27, 2026
3 min read

CORS controls how browsers allow cross-origin requests between different domains.

How CORS Works#

Browser (https://app.com) Server (https://api.com) │ │ │──── Preflight (OPTIONS) ──────────▶│ │ Origin: https://app.com │ │ Access-Control-Request-Method │ │ │ │◀─── CORS Headers ─────────────────│ │ Access-Control-Allow-Origin │ │ Access-Control-Allow-Methods │ │ │ │──── Actual Request (POST) ────────▶│ │ Origin: https://app.com │ │ │ │◀─── Response + CORS Headers ──────│

Simple vs Preflight Requests#

1// Simple request (no preflight) 2// GET, HEAD, POST with simple headers 3fetch('https://api.com/data'); 4 5// Preflight required 6// Custom headers, PUT/DELETE, JSON content-type 7fetch('https://api.com/data', { 8 method: 'POST', 9 headers: { 10 'Content-Type': 'application/json', 11 'Authorization': 'Bearer token', 12 }, 13 body: JSON.stringify({ data: 'value' }), 14});

Express CORS Configuration#

1import cors from 'cors'; 2 3// Basic - allow all origins (development only!) 4app.use(cors()); 5 6// Production configuration 7app.use(cors({ 8 origin: ['https://app.com', 'https://admin.app.com'], 9 methods: ['GET', 'POST', 'PUT', 'DELETE'], 10 allowedHeaders: ['Content-Type', 'Authorization'], 11 credentials: true, 12 maxAge: 86400, // Cache preflight for 24 hours 13})); 14 15// Dynamic origin validation 16app.use(cors({ 17 origin: (origin, callback) => { 18 const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || []; 19 20 // Allow requests with no origin (mobile apps, curl) 21 if (!origin) return callback(null, true); 22 23 if (allowedOrigins.includes(origin)) { 24 callback(null, true); 25 } else { 26 callback(new Error('Not allowed by CORS')); 27 } 28 }, 29 credentials: true, 30}));

Manual CORS Headers#

1app.use((req, res, next) => { 2 const origin = req.headers.origin; 3 const allowedOrigins = ['https://app.com']; 4 5 if (allowedOrigins.includes(origin)) { 6 res.setHeader('Access-Control-Allow-Origin', origin); 7 } 8 9 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); 10 res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 11 res.setHeader('Access-Control-Allow-Credentials', 'true'); 12 res.setHeader('Access-Control-Max-Age', '86400'); 13 14 // Handle preflight 15 if (req.method === 'OPTIONS') { 16 return res.status(204).end(); 17 } 18 19 next(); 20});

Common CORS Headers#

1// Response headers (server sets these) 2'Access-Control-Allow-Origin': 'https://app.com' // or '*' 3'Access-Control-Allow-Methods': 'GET, POST, PUT' 4'Access-Control-Allow-Headers': 'Content-Type' 5'Access-Control-Allow-Credentials': 'true' 6'Access-Control-Max-Age': '86400' 7'Access-Control-Expose-Headers': 'X-Custom-Header' 8 9// Request headers (browser sets these) 10'Origin': 'https://app.com' 11'Access-Control-Request-Method': 'POST' 12'Access-Control-Request-Headers': 'Content-Type'

Credentials and Cookies#

1// Server must allow credentials 2app.use(cors({ 3 origin: 'https://app.com', // Can't use '*' with credentials 4 credentials: true, 5})); 6 7// Client must include credentials 8fetch('https://api.com/data', { 9 credentials: 'include', 10});

Common Issues#

1// ❌ Wildcard with credentials 2res.setHeader('Access-Control-Allow-Origin', '*'); 3res.setHeader('Access-Control-Allow-Credentials', 'true'); 4// Error: Cannot use wildcard with credentials 5 6// ✅ Specific origin with credentials 7res.setHeader('Access-Control-Allow-Origin', 'https://app.com'); 8res.setHeader('Access-Control-Allow-Credentials', 'true'); 9 10// ❌ Missing Content-Type in allowed headers 11fetch('/api', { 12 headers: { 'Content-Type': 'application/json' }, 13}); 14// Preflight fails if Content-Type not in Access-Control-Allow-Headers 15 16// ✅ Include all required headers 17res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

Debugging CORS#

1// Browser console shows CORS errors 2// Check Network tab for preflight (OPTIONS) requests 3 4// Server-side logging 5app.use((req, res, next) => { 6 console.log('Origin:', req.headers.origin); 7 console.log('Method:', req.method); 8 console.log('Headers:', req.headers); 9 next(); 10});

CORS protects users by restricting cross-origin requests. Configure it properly for security and functionality.

Share this article

Help spread the word about Bootspring