Introduction to CORS
Cross-Origin Resource Sharing (CORS) is a security mechanism built into modern browsers that controls how web pages in one domain can request resources from another domain. CORS extends and adds flexibility to the Same-Origin Policy (SOP), which otherwise prevents websites from making cross-origin HTTP requests. Understanding CORS is essential for developing secure web applications, especially when building APIs or frontend applications that interact with third-party services.
Core CORS Concepts
Same-Origin Policy (SOP)
- Definition: Security feature restricting web pages from making requests to domains different from their origin
- Origin components: Protocol (http/https), domain, and port
- Example: A page from
https://example.com
cannot make requests tohttps://api.com
without CORS
CORS Headers
Header | Purpose | Example |
---|---|---|
Access-Control-Allow-Origin | Specifies which origins can access the resource | Access-Control-Allow-Origin: https://example.com |
Access-Control-Allow-Methods | Specifies allowed HTTP methods | Access-Control-Allow-Methods: GET, POST, PUT |
Access-Control-Allow-Headers | Specifies allowed request headers | Access-Control-Allow-Headers: Content-Type, Authorization |
Access-Control-Allow-Credentials | Indicates if requests can include credentials | Access-Control-Allow-Credentials: true |
Access-Control-Expose-Headers | Headers that browsers are allowed to access | Access-Control-Expose-Headers: X-Custom-Header |
Access-Control-Max-Age | Duration preflight results can be cached | Access-Control-Max-Age: 3600 |
Request Types
Simple Requests
- Don’t trigger preflight checks
- Must use
GET
,HEAD
, orPOST
methods - Only allowed headers:
Accept
,Accept-Language
,Content-Language
,Content-Type
- Content-Type limited to:
application/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
Preflighted Requests
- Require an OPTIONS request before the actual request
- Triggered by: custom headers, non-simple methods (PUT, DELETE), or complex Content-Types (JSON)
- Server must respond to OPTIONS with appropriate CORS headers
Credentialed Requests
- Include cookies or HTTP authentication
- Require
withCredentials: true
in request configuration - Server must respond with
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin
cannot be wildcard (*
), must specify exact origin
CORS Implementation Process
Server-Side Implementation
Simple Server Configuration:
- Add appropriate CORS headers to all responses
- Set
Access-Control-Allow-Origin
to allowed origins
Handling Preflight Requests:
- Listen for OPTIONS requests
- Respond with CORS headers but no body content
- Return 200/204 status code
Credential Handling:
- When supporting credentials, set
Access-Control-Allow-Credentials: true
- Specify exact origin in
Access-Control-Allow-Origin
- When supporting credentials, set
Client-Side Implementation
Making Simple Cross-Origin Requests:
fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data));
Making Credentialed Requests:
fetch('https://api.example.com/data', { credentials: 'include' }) .then(response => response.json()) .then(data => console.log(data));
Using XMLHttpRequest with CORS:
const xhr = new XMLHttpRequest(); xhr.open('GET', 'https://api.example.com/data'); xhr.withCredentials = true; // For credentialed requests xhr.send();
Framework-Specific CORS Configuration
Express.js (Node.js)
// Simple CORS middleware
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.status(204).send('');
}
next();
});
// Or use the cors package
const cors = require('cors');
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
Spring Boot (Java)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "Authorization")
.allowCredentials(true)
.maxAge(3600);
}
}
Django (Python)
# settings.py
INSTALLED_APPS = [
# ...
'corsheaders',
# ...
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# ... other middleware
]
CORS_ALLOWED_ORIGINS = [
"https://example.com",
]
CORS_ALLOW_METHODS = [
"GET",
"POST",
"PUT",
"DELETE",
"OPTIONS",
]
CORS_ALLOW_HEADERS = [
"Content-Type",
"Authorization",
]
CORS_ALLOW_CREDENTIALS = True
Common CORS Errors and Solutions
Error | Description | Solution |
---|---|---|
“Cross-Origin Request Blocked” | Browser blocked request due to CORS policy | Add proper CORS headers on server |
“Origin not allowed” | Request origin not in allowed list | Add origin to Access-Control-Allow-Origin |
“Method not allowed” | HTTP method not permitted | Add method to Access-Control-Allow-Methods |
“Request header not allowed” | Header not in allowed list | Add header to Access-Control-Allow-Headers |
“Credentials not supported” | Using credentials with wildcard origin | Specify exact origin instead of * |
“Preflight response invalid” | OPTIONS response missing required headers | Ensure proper CORS headers in preflight response |
CORS Security Best Practices
- Avoid wildcard origins: Use specific domains in
Access-Control-Allow-Origin
instead of*
- Limit allowed methods: Only specify HTTP methods your API actually supports
- Restrict allowed headers: Only allow headers your application needs
- Set reasonable max-age: Balance between performance and security updates
- Validate Origin header: Implement server-side validation of the Origin header
- Use HTTPS: Always use HTTPS for cross-origin requests to prevent man-in-the-middle attacks
- Apply rate limiting: Protect your API from abuse with rate limiting
- Implement token-based auth: Use tokens instead of cookies when possible
Debugging CORS Issues
- Check browser console: Look for specific CORS error messages
- Verify server responses: Use network tab to see if proper headers are being returned
- Test with curl: Bypass browser restrictions to isolate server issues
curl -X OPTIONS https://api.example.com/data -H "Origin: https://example.com" -v
- Use Chrome CORS extension: Temporarily disable CORS for testing (never in production)
- Check for proxy issues: Verify if intermediate proxies strip CORS headers
Resources for Further Learning
- MDN Web Docs on CORS
- CORS specification (W3C)
- CORS Guide on web.dev
- OWASP CORS Security Cheatsheet
- Testing CORS with Postman
Tools for CORS Testing and Implementation
Remember that CORS is a security feature, not a bug! Proper implementation protects both your users and your services.