CORS: Complete Guide to Access-Control-Allow
CORS: The Complete Guide to Access-Control-Allow
CORS (Cross-Origin Resource Sharing) is how a server opts the browser in to using its resources from a different origin. Without CORS, any JS on another domain cannot read your API документацию's response — that is the foundation of the Same-Origin Policy. This article walks through preflight, the Access-Control-Allow-* headers, credentials, and the infamous "has been blocked by CORS policy" errors.
Same-Origin Policy and the need for CORS
An origin is a (scheme, host, port) tuple. https://app.example.com and https://api.example.com are different origins. By default the browser refuses to let JavaScript read cross-origin fetch responses. CORS is the official way for the server to say "yes, I'm fine with this." The protocol is defined in the WHATWG Fetch Standard.
Simple requests
A request is "simple" when the method is GET/POST/HEAD, there are no custom headers, and Content-Type is application/x-www-form-urlencoded, multipart/form-data, or text/plain. The server responds:
Access-Control-Allow-Origin: https://app.example.com
Vary: Origin
Vary: Origin is critical when the allowed origin is dynamic — otherwise the CDN will cache the response for the first origin and serve it to everyone else.
Preflight OPTIONS
Complex requests (PUT/DELETE/PATCH, custom headers, JSON body) are preceded by an OPTIONS request. The server must answer with the permission headers:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH
Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Key
Access-Control-Max-Age: 86400
Max-Age caches the preflight in the browser, saving 50+ ms per request.
Credentials (cookies, Authorization)
To send cookies or an Authorization header the client calls fetch(..., { credentials: 'include' }) and the server answers:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Crucial: with Allow-Credentials: true you cannot use Access-Control-Allow-Origin: * — only an explicit origin. This is a data-exfil guardrail.
nginx
map $http_origin $cors_origin {
default "";
"https://app.example.com" $http_origin;
"https://admin.example.com" $http_origin;
}
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Max-Age "86400" always;
add_header Vary "Origin" always;
return 204;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Vary "Origin" always;
proxy_pass http://backend;
}
Express and NestJS
// Express
import cors from 'cors';
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
credentials: true,
methods: ['GET','POST','PUT','DELETE','PATCH'],
maxAge: 86400,
}));
// NestJS
app.enableCors({
origin: (origin, cb) => {
const allow = ['https://app.example.com'];
if (!origin || allow.includes(origin)) cb(null, true);
else cb(new Error('CORS blocked'));
},
credentials: true,
});
Common mistakes
"No Access-Control-Allow-Origin header" — the server never set it. Verify with curl -I -H "Origin: https://app.example.com" https://api.example.com/.
"Wildcard with credentials" — either drop credentials or replace * with an explicit origin.
Cache serves the wrong Allow-Origin — add Vary: Origin.
OPTIONS returns 401 — preflight travels without auth tokens; whitelist OPTIONS in your auth middleware.
Debugging
Browser DevTools → Network → inspect the preflight. In the terminal:
curl -i -X OPTIONS \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type,Authorization" \
https://api.example.com/endpoint
The enterno.io CORS Checker simulates any Origin and shows the server response without touching frontend code.
FAQ
Is CORS a server protection? No. CORS is a browser restriction that the server can relax. curl and any server-side HTTP client ignore it.
Can I use Access-Control-Allow-Origin: *? Only for public APIs without credentials — CDN assets, open data.
What breaks preflight most often? A missing Access-Control-Allow-Headers for Authorization or X-API-Key.
Does same-origin need CORS? No — same-origin traffic bypasses CORS completely.
Conclusion
CORS is a declaration, not an antivirus. Keep an explicit allowlist, always ship Vary: Origin, never combine * with credentials, monitor header changes with enterno monitors, and use the CORS Checker. See also: HTTP Security Headers, API rate limiting.
Check your website right now
Check now →