CORS Error
Browser Security - Cross-origin request blocked
CORS Errors Explained
What is CORS?
CORS (Cross-Origin Resource Sharing) is a browser security feature that restricts web pages from making requests to a different domain than the one serving the page. When blocked, you'll see an error like:
Access to fetch at 'https://api.example.com/data' from origin
'https://myapp.com' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present.
Why Does This Happen?
Same-Origin Policy
Browsers block requests where origin (protocol + domain + port) differs between the page and the API. This prevents malicious sites from stealing data.
https://app.com→https://api.comBlockedhttp://app.com→https://app.comBlocked (different protocol)https://app.com:3000→https://app.com:8080Blocked (different port)
Common CORS Errors
Missing Access-Control-Allow-Origin
Server doesn't include the required CORS header in response.
Preflight Request Failed
OPTIONS request returned an error or wrong headers.
Credentials Not Supported
Trying to send cookies but server doesn't allow credentials.
Server-Side Fixes
Express.js
const cors = require('cors');
// Allow all origins (development only!)
app.use(cors());
// Production: specific origins
app.use(cors({
origin: ['https://myapp.com', 'https://www.myapp.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE']
}));
Nginx
location /api/ {
add_header 'Access-Control-Allow-Origin' 'https://myapp.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
}
Django
# settings.py
INSTALLED_APPS = [..., 'corsheaders']
MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware', ...]
CORS_ALLOWED_ORIGINS = [
'https://myapp.com',
]
CORS_ALLOW_CREDENTIALS = True
Required Headers
| Header | Purpose |
|---|---|
| Access-Control-Allow-Origin | Which origins can access |
| Access-Control-Allow-Methods | Allowed HTTP methods |
| Access-Control-Allow-Headers | Allowed request headers |
| Access-Control-Allow-Credentials | Allow cookies/auth headers |
Preflight Requests
For non-simple requests (custom headers, PUT/DELETE methods), browsers send an OPTIONS request first:
# Browser sends preflight
OPTIONS /api/users HTTP/1.1
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
# Server must respond with:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization
Common Mistakes
- Using * with credentials: Can't use wildcard origin with cookies
- Missing OPTIONS handler: Preflight returns 404 or 405
- Headers not exposed: Custom response headers need Access-Control-Expose-Headers
- Caching issues: Browser cached old CORS response
Debugging Tips
- Check the Network tab for the actual response headers
- Look for the preflight OPTIONS request
- Test with curl (bypasses CORS):
curl -I https://api.example.com - Server-side errors may also cause CORS to fail (fix the 500 first)