Cross-Origin Resource Sharing (CORS) controls which websites can access your API. Misconfiguration leads to security vulnerabilities or broken applications.
How CORS Works#
Same-Origin Policy:
- Browser security feature
- Blocks cross-origin requests by default
- Origin = protocol + domain + port
CORS:
- Server opts in to cross-origin requests
- Uses HTTP headers
- Browser enforces the policy
Example Origins:
https://example.com - different from:
https://api.example.com - different subdomain
http://example.com - different protocol
https://example.com:8080 - different port
Simple vs Preflight Requests#
Simple Requests (no preflight):
- GET, HEAD, POST
- Only simple headers (Accept, Content-Type, etc.)
- Content-Type: text/plain, multipart/form-data, application/x-www-form-urlencoded
Preflight Required:
- PUT, DELETE, PATCH
- Custom headers
- Content-Type: application/json
- Credentials (cookies)
Preflight Flow:
1. Browser sends OPTIONS request
2. Server responds with allowed origins/methods
3. Browser sends actual request
4. Server processes request
CORS Headers#
Response Headers:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Access-Control-Expose-Headers: X-Custom-Header
Request Headers (set by browser):
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
Express CORS Configuration#
Dynamic Origin Validation#
Manual CORS Implementation#
Next.js API Routes#
Common Pitfalls#
Debugging CORS#
Security Checklist#
Configuration:
✓ Explicitly list allowed origins
✓ Never use wildcard with credentials
✓ Validate dynamic origins carefully
✓ Set appropriate Max-Age
Headers:
✓ Only expose necessary headers
✓ Only allow necessary methods
✓ Validate Content-Type for POST/PUT
✓ Consider Access-Control-Max-Age
Monitoring:
✓ Log blocked CORS requests
✓ Alert on unexpected origins
✓ Review allowed origins regularly
✓ Test CORS in staging
Best Practices#
Development:
- Use proxy in development to avoid CORS
- Test with production CORS settings
- Don't disable CORS for convenience
Production:
- Whitelist specific origins
- Use HTTPS only origins
- Regular security audits
- Monitor for abuse
Conclusion#
CORS protects your API from unauthorized cross-origin access. Always explicitly list allowed origins, never reflect arbitrary origins, and test thoroughly. When credentials are involved, be especially careful—wildcard origins won't work and reflecting origins is dangerous.