CORS Explained: Cross-Origin Resource Sharing and Browser Security
How CORS works — preflight requests, Access-Control headers, why browsers block cross-origin requests, and how to configure CORS correctly for your API.
CORS (Cross-Origin Resource Sharing)
CORS is a browser security mechanism that controls which web applications on one origin (domain) are allowed to request resources from a different origin, using HTTP headers to grant or deny access.
What It Really Means
When your frontend at https://app.example.com makes an API call to https://api.example.com, the browser blocks it by default. This is the Same-Origin Policy — a fundamental browser security feature that prevents malicious websites from making requests to your bank's API using your cookies.
CORS is the mechanism for relaxing this restriction. The API server sends HTTP headers telling the browser: "I allow requests from app.example.com." Without these headers, the browser blocks the response even though the server processed the request successfully.
CORS is confusing because it is enforced by the browser, not the server. The server always receives and processes the request. The browser decides whether to let your JavaScript see the response. This is why the same API works fine from Postman or curl but fails from a browser — those tools do not enforce CORS.
How It Works in Practice
Simple Requests (No Preflight)
For simple requests (GET, POST with standard content types), the browser sends the request directly and checks CORS headers on the response:
Preflight Requests
For "non-simple" requests (PUT, DELETE, custom headers, JSON content type), the browser sends an OPTIONS preflight request first:
Key CORS Headers
| Header | Purpose | Example |
|---|---|---|
| Access-Control-Allow-Origin | Which origins can access | https://app.example.com |
| Access-Control-Allow-Methods | Which HTTP methods allowed | GET, POST, PUT, DELETE |
| Access-Control-Allow-Headers | Which request headers allowed | Authorization, Content-Type |
| Access-Control-Allow-Credentials | Allow cookies/auth | true |
| Access-Control-Max-Age | Cache preflight (seconds) | 86400 |
| Access-Control-Expose-Headers | Which response headers JS can read | X-Request-Id |
Implementation
Express.js CORS configuration:
Nginx CORS headers:
Trade-offs
Security vs convenience:
Access-Control-Allow-Origin: *allows any website to access your API. Never use this withcredentials: true.- Restricting to specific origins requires maintaining an allowlist. Dynamic validation is more flexible but more complex.*
Preflight overhead:
- Every non-simple request incurs an extra OPTIONS round trip
Access-Control-Max-Agecaches preflight results (set to 86400 for 24 hours)- In high-frequency API calls, preflight overhead is measurable
Avoiding CORS entirely:
- Same-origin deployment: Serve API and frontend from the same domain
- Reverse proxy: Nginx proxies /api/* to the backend, making it same-origin from the browser's perspective
- BFF (Backend For Frontend): A same-origin backend that proxies API calls*
Common Misconceptions
- "CORS protects the server" — CORS protects the user's browser. The server always processes the request. CORS prevents malicious JavaScript from reading the response.
- "CORS errors mean the request failed" — The server received and processed the request. The browser blocked JavaScript from seeing the response. Check server logs — the request succeeded.
- "Access-Control-Allow-Origin: * with credentials works" — It does not. Browsers reject this combination. You must specify the exact origin when using credentials.
- "Disabling CORS in the browser is a valid fix" — It is a development workaround only. It removes a security protection for the user. The server must send correct CORS headers in production.
- "Server-to-server calls need CORS" — CORS is a browser-only mechanism. Requests from servers (curl, Postman, backend services) are not subject to CORS restrictions.*
How This Appears in Interviews
- "Your frontend gets a CORS error calling your API" — Check that the API server sends correct Access-Control-Allow-Origin header. For non-simple requests, ensure OPTIONS preflight returns the right headers.
- "Design a public API" — Discuss CORS policy for third-party consumers. Rate limiting and API keys replace CORS for server-to-server authentication.
- "How do you secure an API that serves multiple frontends?" — Dynamic origin validation against an allowlist, credentials mode for authenticated requests, same-site cookies.
- "What is a preflight request?" — Browser sends OPTIONS before non-simple requests to check if the server allows the method, headers, and origin.
Related Concepts
- TLS/SSL Handshake — CORS operates within HTTPS-secured connections
- CDN and Edge Computing — CDN responses must include correct CORS headers
- What Happens When You Type a URL — CORS is part of browser request processing
- HTTP/2 Multiplexing — preflight requests benefit from HTTP/2
- System Design Interview Guide
- Algoroq Pricing — access all concept deep-dives
GO DEEPER
Learn from senior engineers in our 12-week cohort
Our Advanced System Design cohort covers this and 11 other deep-dive topics with live sessions, assignments, and expert feedback.