What is CORS
CORS (Cross-Origin Resource Sharing) is a browser mechanism for safely sharing resources across different origins. For security reasons, browsers restrict requests to different origins by default.
What is an Origin: A combination of scheme (http/https), host, and port.
https://example.com:443andhttps://api.example.com:443are different origins.
Same-Origin Policy
Browsers restrict requests to different origins through the Same-Origin Policy (SOP).
From https://app.example.com:
| URL | Result | Reason |
|---|---|---|
| https://app.example.com/api | ✓ Allowed | Same origin |
| https://api.example.com/v1 | ✗ Blocked | Different host |
| http://app.example.com/api | ✗ Blocked | Different scheme |
| https://app.example.com:8080 | ✗ Blocked | Different port |
Basic CORS Flow
Simple Requests
Requests meeting the following conditions are sent without preflight.
- Methods: GET, HEAD, POST
- Headers: Accept, Accept-Language, Content-Language, Content-Type (limited)
- Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain
sequenceDiagram
participant Browser
participant Server
Browser->>Server: GET /api/users<br/>Origin: https://app.example.com
Server->>Browser: HTTP/1.1 200 OK<br/>Access-Control-Allow-Origin: https://app.example.com
Preflight Requests
Requests with custom headers or JSON require pre-verification.
sequenceDiagram
participant Browser
participant Server
Note over Browser, Server: 1. Preflight (Pre-verification)
Browser->>Server: OPTIONS /api/users<br/>Origin: https://app.example.com<br/>Access-Control-Request-Method: POST<br/>Access-Control-Request-Headers: Content-Type, Authorization
Server->>Browser: HTTP/1.1 204 No Content<br/>Access-Control-Allow-Origin: https://app.example.com<br/>Access-Control-Allow-Methods: POST, GET, OPTIONS<br/>Access-Control-Allow-Headers: Content-Type, Authorization<br/>Access-Control-Max-Age: 86400
Note over Browser, Server: 2. Actual Request
Browser->>Server: POST /api/users<br/>Origin: https://app.example.com<br/>Content-Type: application/json<br/>Authorization: Bearer token123
CORS Headers
Response Headers
| Header | Description |
|---|---|
| Access-Control-Allow-Origin | Allowed origin |
| Access-Control-Allow-Methods | Allowed HTTP methods |
| Access-Control-Allow-Headers | Allowed request headers |
| Access-Control-Allow-Credentials | Allow cookie transmission |
| Access-Control-Expose-Headers | Headers accessible from JS |
| Access-Control-Max-Age | Preflight result cache time |
Configuration Example (Express.js)
const cors = require('cors');
// Allow specific origin
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
// Allow multiple origins
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
}));
// Dynamically validate origin
app.use(cors({
origin: (origin, callback) => {
const allowedOrigins = ['https://app.example.com'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));
Credentials Mode
Requests with cookies or authentication require special configuration.
// Client side
fetch('https://api.example.com/data', {
credentials: 'include' // Include cookies
});
// Server side
// Access-Control-Allow-Origin: * cannot be used
// Must specify concrete origin
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Common Errors and Solutions
| Error | Cause | Solution |
|---|---|---|
| No ‘Access-Control-Allow-Origin’ header | Server not returning CORS headers | Configure CORS on server side, or route requests through a proxy |
| Wildcard with credentials | Combination of credentials: 'include' and Access-Control-Allow-Origin: * | Specify concrete origin: Access-Control-Allow-Origin: https://app.example.com |
| Method not allowed | Method not allowed in preflight | Add required methods to Access-Control-Allow-Methods |
| Header not allowed | Custom header not allowed | Add header to Access-Control-Allow-Headers |
Development CORS Solutions
Using a Proxy
// Vite configuration example
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
});
Browser Extensions
Extensions that disable CORS are available for development only. Do not use in production.
Security Considerations
Wildcard Dangers
| Configuration | Risk Level | Description |
|---|---|---|
Access-Control-Allow-Origin: * | ✗ Dangerous | Allow all origins |
Access-Control-Allow-Origin: https://app.example.com | ✓ Recommended | Explicitly specify allowed origins |
Origin Validation
// ✗ Bad example: Return request Origin as-is
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
// ✓ Good example: Validate against whitelist
const allowedOrigins = ['https://app.example.com'];
if (allowedOrigins.includes(req.headers.origin)) {
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
}
Summary
CORS is an important mechanism that enables resource sharing across different origins while maintaining web application security. By properly configuring headers and validating origins, you can achieve secure cross-origin communication.
← Back to list