CORS
CORS is a browser security mechanism that controls which cross-origin requests are allowed. Browsers block requests to different domains by default. CORS lets servers explicitly permit them by returning Access-Control-Allow-Origin and related headers.
What CORS is and why it exists
By default, browsers enforce the Same-Origin Policy — scripts on app.example.com cannot fetch data from api.other.com. This prevents malicious scripts from silently accessing APIs on your behalf.
CORS is the controlled exception. A server can declare which origins it trusts by returning the right headers. The browser checks those headers before allowing the response to reach your JavaScript.
The headers that make CORS work
| Header | Direction | What it does |
|---|---|---|
Access-Control-Allow-Origin | Response | Which origin is allowed. Must match the request Origin exactly, or * |
Access-Control-Allow-Methods | Response (preflight) | Which HTTP methods are allowed |
Access-Control-Allow-Headers | Response (preflight) | Which request headers are allowed (Content-Type, Authorization, etc.) |
Access-Control-Allow-Credentials | Response | Whether cookies and auth headers can be included |
Access-Control-Max-Age | Response (preflight) | How long to cache preflight results (seconds) |
Simple vs preflighted requests
Simple requests (GET, POST with text/plain or form data) go directly. The browser checks the response headers after.
Preflighted requests (POST with JSON, PUT, DELETE, custom headers) first send an OPTIONS request. The browser only sends the actual request if the preflight response has matching CORS headers.
Minimal working CORS — Nginx
# Handle preflight
if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin "https://app.example.com"; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Content-Type, Authorization"; add_header Access-Control-Max-Age 86400; return 204;
}
add_header Access-Control-Allow-Origin "https://app.example.com" always;
The wildcard trap
Using Access-Control-Allow-Origin: * is fine for public APIs with no authentication. Never combine it with Access-Control-Allow-Credentials: true — browsers reject this combination because it would let any origin access authenticated responses.