CORS - Cross-Origin Resource Sharing
CORS is a browser mechanism that relaxes the same-origin policy: by default a page can't read responses from a different origin, and CORS headers let a server allow specific origins to do so.
What is an origin
https://app.example.com:443/path
|_____| |_____________| |__|
scheme host port
// same origin
https://app.example.com/other-path - same
// different origin (cross-origin)
http://app.example.com - different scheme
https://api.example.com - different host
https://app.example.com:8080 - different portSimple requests
GET, HEAD or POST with basic headers and form/text content types - the browser sends the request right away and then checks the response headers. If they don't allow the origin, the request still reached the server, but JavaScript can't read the response.
Request: GET https://api.example.com/data
Origin: https://app.example.com
Response: Access-Control-Allow-Origin: https://app.example.com
(or * for any origin)Preflight requests
For non-simple requests (PUT, DELETE, custom headers) - browser sends an OPTIONS request first.
Preflight OPTIONS:
Origin: https://app.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization
Server response:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400 (cache preflight for 24h)Server configuration (Express)
import cors from 'cors';
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Authorization', 'Content-Type'],
credentials: true, // allow cookies
}));Credentials and cookies
// client must opt in
fetch('https://api.example.com/data', { credentials: 'include' });
// server must explicitly allow
// Access-Control-Allow-Credentials: true
// Access-Control-Allow-Origin must NOT be * when credentials: trueCORS is enforced by the browser, not the server. Direct requests from curl or Postman bypass it entirely. The server only needs to send the right headers.
updated