CORS (Cross-Origin Resource Sharing) is a browser security feature that controls which websites can access your API. By default, browsers block requests from one origin to another - CORS headers tell browsers when to allow these cross-origin requests.
CORS
Cross-Origin Resource Sharing - A security mechanism that controls how web pages can request resources from different domains.
Why CORS Exists
Same-Origin Policy: Browsers block requests to different domains by default. This prevents malicious sites from stealing data via your logged-in sessions.
Origin = protocol + domain + port:
https://example.com:443 is different from http://example.com or https://api.example.com
CORS relaxes this restriction: Server sends headers saying "I allow requests from these origins."
How CORS Works
Simple requests (GET, POST with simple headers):
- Browser sends request with
Originheader - Server responds with
Access-Control-Allow-Origin - Browser checks if origin is allowed
- If allowed, JavaScript gets response. If not, error.
Preflight requests (PUT, DELETE, custom headers):
- Browser sends OPTIONS request first
- Server responds with allowed methods and headers
- If allowed, browser sends actual request
- If not, actual request is blocked
CORS Headers Explained
| Header | Purpose | Example |
|---|---|---|
Access-Control-Allow-Origin | Which origins can access | https://myapp.com or * |
Access-Control-Allow-Methods | Allowed HTTP methods | GET, POST, PUT, DELETE |
Access-Control-Allow-Headers | Allowed request headers | Content-Type, Authorization |
Access-Control-Allow-Credentials | Allow cookies/auth | true |
Access-Control-Max-Age | Cache preflight (seconds) | 86400 |
Access-Control-Expose-Headers | Headers JS can read | X-Total-Count |
Common CORS Configurations
Public API (no auth):
Access-Control-Allow-Origin: *
Anyone can call your API. Fine for public data.
Specific origins:
Access-Control-Allow-Origin: https://myapp.com
Only your app can call the API. Check origin dynamically for multiple allowed domains.
With credentials:
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Credentials: true
Required for cookies. Cannot use * with credentials.
Preflight Requests
Browsers send an OPTIONS preflight for:
- Methods other than GET, HEAD, POST
- Custom headers (like Authorization)
- Content-Type other than form-urlencoded, multipart, text/plain
Optimize with Max-Age:
Preflight adds latency. Cache preflight response:
Access-Control-Max-Age: 86400 (24 hours)
Common CORS Errors
"No 'Access-Control-Allow-Origin' header": Server didn't include CORS headers. Add them.
"Credential is not supported if the CORS header is '*'": Using credentials with wildcard origin. Specify exact origin.
"Method not allowed": Server didn't list the method in Allow-Methods.
"Header not allowed": Server didn't list the header in Allow-Headers. Add Authorization if using JWT.
Security Best Practices
Never reflect Origin header blindly: Don't copy request Origin to response. Validates against whitelist.
Be specific with origins:
* is fine for public APIs. Use explicit origins for private APIs.
Don't allow credentials with wildcard: Browsers block this anyway, but it signals misconfiguration.
Limit allowed methods: Only allow methods your API actually uses.
Validate origins server-side: CORS is browser-enforced. Server-to-server requests bypass it. Always authenticate.
CORS vs Authentication
CORS doesn't replace authentication: CORS controls browser requests. APIs are still accessible via curl, Postman, or server code. Always require authentication for protected endpoints.
CORS is not security by itself: It prevents malicious websites from using your users' sessions. It's not a general-purpose access control.
Common Mistakes
1. Adding CORS only to GET: OPTIONS preflight needs CORS headers too. Apply to all routes.
2. Forgetting Authorization header:
If using JWT, add Authorization to Access-Control-Allow-Headers.
3. Wildcard with credentials:
Access-Control-Allow-Origin: * with credentials fails. Use specific origin.
4. Not handling OPTIONS: Preflight requests need 200 response with CORS headers.
5. Development vs production:
localhost:3000 in dev, but production origin in prod. Configure per environment.
Debugging CORS
Browser DevTools: Network tab shows CORS headers. Console shows specific error.
Check preflight: Filter network tab for OPTIONS requests. Check response headers.
Test without browser: curl ignores CORS. If curl works but browser doesn't, it's CORS.
Code Examples
CORS Configuration in Express
import cors from 'cors';
// Simple: Allow all origins
app.use(cors());
// Specific origins
const allowedOrigins = [
'https://myapp.com',
'https://staging.myapp.com',
process.env.NODE_ENV === 'development' && 'http://localhost:3000'
].filter(Boolean);
app.use(cors({
origin: (origin, callback) => {
// Allow requests with no origin (mobile apps, curl)
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
callback(null, origin);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // Cache preflight for 24 hours
}));
// Manual CORS (without middleware)
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
res.setHeader('Access-Control-Max-Age', '86400');
return res.status(200).end();
}
next();
});Related Terms
Mock Server
A fake server that simulates API responses for testing and development purposes.
API Testing
The process of verifying that APIs work correctly, securely, and perform well.
OpenAPI Specification
A standard format for describing REST APIs, enabling documentation and code generation.
JWT (JSON Web Token)
A compact, URL-safe token format for securely transmitting information between parties.