Topic Overview
Cross-Origin Resource Sharing (CORS)
Master CORS: how browsers handle cross-origin requests, preflight requests, and security policies for web applications.
Cross-Origin Resource Sharing (CORS)
Why This Matters
Think of CORS like a bouncer at a club. The club (server) has a policy about who can enter. If you're from the same neighborhood (same origin), you can enter freely. If you're from a different neighborhood (cross-origin), the bouncer checks the list (CORS headers) to see if you're allowed. CORS does the same for web requests—it's a browser security policy that controls cross-origin requests.
This matters because browsers enforce the same-origin policy by default—scripts from one origin can't access resources from another origin. This prevents attacks (like CSRF), but it also blocks legitimate cross-origin requests. CORS allows servers to specify which origins can access their resources, enabling cross-origin requests while maintaining security.
In interviews, when someone asks "Why can't my frontend call my API?", they're testing whether you understand CORS. Do you know what same-origin means? Do you understand CORS headers? Most engineers don't. They just see CORS errors and don't know how to fix them.
What Engineers Usually Get Wrong
Most engineers think "CORS is a server setting." But CORS is a browser security policy. The server sends CORS headers, and the browser enforces the policy. If CORS headers aren't set correctly, the browser blocks the request. Understanding this helps you configure CORS correctly and debug CORS issues.
Engineers also don't understand preflight requests. For certain requests (like PUT with custom headers), browsers send a preflight OPTIONS request first to check if the actual request is allowed. If the preflight fails, the actual request never happens. Understanding this helps you debug why requests fail even though the endpoint works.
How This Breaks Systems in the Real World
A service had a frontend (example.com) and an API (api.example.com). The frontend tried to call the API, but the browser blocked it (CORS error). The API didn't send CORS headers allowing example.com. The fix? Configure CORS on the API server. Send Access-Control-Allow-Origin: https://example.com header. This allows the frontend to call the API.
Another story: A service was using Access-Control-Allow-Origin: * (allow all origins) for development, but forgot to restrict it in production. This allowed any website to call the API, creating a security risk. The fix? Only allow specific origins in production. Use Access-Control-Allow-Origin: https://yourdomain.com instead of *. This prevents unauthorized cross-origin access.
What is CORS?
CORS enables:
- Cross-origin requests: Requests from one origin to another
- Controlled access: Server controls which origins can access resources
- Security: Prevents unauthorized cross-origin access
Origin: Protocol + Domain + Port
https://example.com:443
http://localhost:3000
Same-origin: Same protocol, domain, and port Cross-origin: Different protocol, domain, or port
Same-Origin Policy
Same-Origin Policy restricts:
- JavaScript: Can only access resources from same origin
- AJAX requests: Blocked to different origins
- Cookies: Not sent to different origins
Why needed:
- Security: Prevents malicious websites from accessing user data
- Privacy: Protects user information
Example:
Page: https://example.com
Request to: https://api.example.com
Result: Blocked (different origin)
CORS Headers
Server Response Headers
1. Access-Control-Allow-Origin
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Origin: * (allow all origins)
2. Access-Control-Allow-Methods
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
3. Access-Control-Allow-Headers
Access-Control-Allow-Headers: Content-Type, Authorization
4. Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true
5. Access-Control-Max-Age
Access-Control-Max-Age: 3600 (cache preflight for 1 hour)
Client Request Headers
1. Origin
Origin: https://example.com
2. Access-Control-Request-Method
Access-Control-Request-Method: POST
3. Access-Control-Request-Headers
Access-Control-Request-Headers: Content-Type
Simple vs Preflight Requests
Simple Requests
Conditions:
- Method: GET, POST, or HEAD
- Headers: Only simple headers (Accept, Content-Language, Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain)
Flow:
Browser → Server: GET /api/data
Origin: https://example.com
Server → Browser: Response
Access-Control-Allow-Origin: https://example.com
Data: {...}
No preflight: Request sent directly
Preflight Requests
Triggered when:
- Method: PUT, DELETE, PATCH, or custom methods
- Headers: Custom headers (Authorization, Content-Type: application/json)
Flow:
Browser → Server: OPTIONS /api/data (preflight)
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
Server → Browser: Preflight Response
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
Browser → Server: POST /api/data (actual request)
Origin: https://example.com
Content-Type: application/json
Server → Browser: Response
Access-Control-Allow-Origin: https://example.com
Data: {...}
Two requests: Preflight (OPTIONS) + Actual request
Examples
CORS Configuration
1import express from 'express';2import cors from 'cors';34const app = express();56// Simple CORS (allow all)7app.use(cors());89// Custom CORS10app.use(cors({11 origin: 'https://example.com',12 methods: ['GET', 'POST', 'PUT', 'DELETE'],13 allowedHeaders: ['Content-Type', 'Authorization'],14 credentials: true,
Manual CORS Headers
1app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {2 const origin = req.headers.origin;34 // Check if origin is allowed5 const allowedOrigins = ['https://example.com', 'https://app.example.com'];67 if (origin && allowedOrigins.includes(origin)) {8 res.setHeader('Access-Control-Allow-Origin', origin);9 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');10 res
CORS with Credentials
1// Client (browser)2fetch('https://api.example.com/data', {3 method: 'POST',4 credentials: 'include', // Include cookies5 headers: {6 'Content-Type': 'application/json'7 },8 body: JSON.stringify({ data: 'value' })9});1011// Server12app.use(cors({13 origin: 'https://example.com',14 credentials: true // Allow credentials15}));
Common Pitfalls
- Wildcard with credentials:
Access-Control-Allow-Origin: *withcredentials: truenot allowed. Fix: Specify exact origin - Not handling preflight: OPTIONS request not handled. Fix: Handle OPTIONS requests, return appropriate headers
- Missing headers: Not allowing required headers. Fix: Include all custom headers in
Access-Control-Allow-Headers - CORS errors in console: Browser blocks request. Fix: Check server CORS configuration, verify origin
- Not caching preflight: Preflight sent for every request. Fix: Set
Access-Control-Max-Age
Interview Questions
Beginner
Q: What is CORS and why is it needed?
A:
CORS (Cross-Origin Resource Sharing) allows web pages to make requests to different domains while maintaining security.
Why needed:
- Same-Origin Policy: Browsers block cross-origin requests by default
- Security: Prevents malicious websites from accessing user data
- Controlled access: Server controls which origins can access resources
How it works:
Page: https://example.com
Request to: https://api.example.com
Browser checks: Different origin (cross-origin)
Server responds: Access-Control-Allow-Origin: https://example.com
Browser allows: Request succeeds
Example:
Without CORS:
Browser blocks: Cross-origin request denied
With CORS:
Server allows: Access-Control-Allow-Origin: https://example.com
Browser allows: Request succeeds
CORS Headers:
Access-Control-Allow-Origin: Which origins can accessAccess-Control-Allow-Methods: Which methods allowedAccess-Control-Allow-Headers: Which headers allowed
Intermediate
Q: Explain the difference between simple and preflight requests. When is each used?
A:
Simple Requests:
Conditions:
- Method: GET, POST, or HEAD
- Headers: Only simple headers (Accept, Content-Language, Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain)
Flow:
Browser → Server: GET /api/data
Origin: https://example.com
Server → Browser: Response
Access-Control-Allow-Origin: https://example.com
No preflight: Request sent directly
Preflight Requests:
Triggered when:
- Method: PUT, DELETE, PATCH, or custom methods
- Headers: Custom headers (Authorization, Content-Type: application/json)
Flow:
1. Browser → Server: OPTIONS /api/data (preflight)
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
2. Server → Browser: Preflight Response
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
3. Browser → Server: POST /api/data (actual request)
Origin: https://example.com
Content-Type: application/json
4. Server → Browser: Response
Access-Control-Allow-Origin: https://example.com
Two requests: Preflight (OPTIONS) + Actual request
Why preflight:
- Safety check: Browser checks if server allows request before sending
- Protection: Prevents unauthorized requests
Senior
Q: Design a CORS system for a multi-tenant API that serves multiple client applications. How do you handle different origins, credentials, and security?
A:
1class MultiTenantCORSSystem {2 private allowedOrigins: Map<string, string[]>; // tenant → origins3 private corsCache: Map<string, CORSConfig>;45 constructor() {6 this.allowedOrigins = new Map();7 this.corsCache = new Map();8 }910 // 1. Dynamic CORS Configuration11 async handleCORS(req: Request, res: Response): Promise<void>
Features:
- Multi-tenant: Different CORS configs per tenant
- Dynamic origins: Load from database
- Credentials: Handle cookies/auth properly
- Preflight caching: Cache preflight responses
- Security: Origin validation, rate limiting
-
HTTP/1 vs HTTP/2 vs HTTP/3 - CORS is an HTTP security mechanism, understanding HTTP explains CORS implementation
-
TLS/SSL Handshake - HTTPS uses TLS, CORS works with HTTPS, understanding TLS helps configure CORS for secure connections
-
OSI Model (7 Layers) - CORS operates at Layer 7 (Application), understanding the OSI model provides context
-
Proxy vs Reverse Proxy - Reverse proxies can handle CORS, understanding proxies helps configure CORS at the edge
-
WebSockets - WebSockets can have CORS considerations, understanding CORS helps configure WebSocket connections
-
CORS: Browser security mechanism for cross-origin requests
-
Same-origin policy: Browsers block cross-origin requests by default
-
Simple requests: GET, POST, HEAD with simple headers (no preflight)
-
Preflight requests: OPTIONS request before actual request (for PUT, DELETE, custom headers)
-
CORS headers: Access-Control-Allow-Origin, Access-Control-Allow-Methods, etc.
-
Credentials: Cannot use wildcard (*) with credentials, must specify origin
-
Security: Validate origins, use whitelist, prevent abuse
-
Best practices: Handle preflight, cache preflight, specify exact origins with credentials
Key Takeaways
CORS: Browser security mechanism for cross-origin requests
Same-origin policy: Browsers block cross-origin requests by default
Simple requests: GET, POST, HEAD with simple headers (no preflight)
Preflight requests: OPTIONS request before actual request (for PUT, DELETE, custom headers)
CORS headers: Access-Control-Allow-Origin, Access-Control-Allow-Methods, etc.
Credentials: Cannot use wildcard (*) with credentials, must specify origin
Security: Validate origins, use whitelist, prevent abuse
Best practices: Handle preflight, cache preflight, specify exact origins with credentials
Related Topics
HTTP/1 vs HTTP/2 vs HTTP/3
CORS is an HTTP security mechanism, understanding HTTP explains CORS implementation
TLS/SSL Handshake
HTTPS uses TLS, CORS works with HTTPS, understanding TLS helps configure CORS for secure connections
OSI Model (7 Layers)
CORS operates at Layer 7 (Application), understanding the OSI model provides context
Proxy vs Reverse Proxy
Reverse proxies can handle CORS, understanding proxies helps configure CORS at the edge
WebSockets
WebSockets can have CORS considerations, understanding CORS helps configure WebSocket connections
What's next?