Cross-Origin Resource Sharing (CORS)
Understand browser-enforced security through the Same-Origin Policy and learn how CORS enables secure, controlled resource sharing across different domains.
The Concept
The web browser is an untrusted environment executing external code. To protect users from malicious sites reading sensitive data from other tabs, browsers enforce strict isolation boundaries. However, modern APIs often require client interfaces running on one domain to query data from a server hosted on another.
Cross-Origin Resource Sharing (CORS) is an HTTP-header-based protocol that allows a server to explicitly declare which foreign origins are permitted to load its resources. Rather than blocking requests outright, the browser queries the target API server for permission before allowing the client-side JavaScript to read the response.
Practical Analogy
Think of CORS as an apartment security intercom system:
- The Same-Origin Policy is like locking the main building gate. By default, residents can walk around inside, but visitors from outside the building are locked out.
- A Cross-Origin Request is like an outside delivery driver arriving at the gate.
- A CORS Preflight is like the delivery driver calling up to a specific apartment using the intercom. The driver asks: "I have a package from delivery-service.com, can I bring it up using the elevator (HTTP method)?"
- The Server Response is like the tenant pressing the buzzer. The tenant verifies the driver's origin, replies "Yes, delivery-service.com is allowed to bring up packages using the elevator," and unlocks the gate. The browser then lets the delivery driver walk inside and hand over the package.
Same-Origin Policy (SOP)
The Same-Origin Policy (SOP) is a core security pillar implemented by web browsers. It prevents client-side scripts running on one website from reading data or interacting with resources on a different website.
An origin is defined by three components:
- Protocol (e.g.
httpvshttps) - Host (e.g.
example.comvsapi.example.com) - Port (e.g.
:80vs:8080)
If any of these three elements differ, the browser classifies the targets as different origins. For example, a script executing on https://my-app.com cannot read the raw HTTP response from https://api.my-app.com or https://my-app.com:8080 by default.
It is vital to understand that SOP does not block the browser from sending requests: a malicious website can still trigger a state-changing POST request to your bank. SOP prevents the malicious site from reading the sensitive response data returned by the bank.
Cross-Origin Resource Sharing (CORS)
While SOP prevents unauthorized data leakage, modern web architectures rely on cross-origin setups (such as separating a static frontend domain from a REST API backend). CORS provides a standard mechanism to selectively bypass SOP limits.
When a browser script initiates an HTTP request to a different origin:
- The browser automatically appends an
Originheader to the request containing the sender's origin (e.g.,Origin: https://my-app.com). - The server receives the request, processes the route, and attaches CORS headers to the response.
- The browser intercepts the response, reads the CORS headers, and checks if the origin is authorized. If the header
Access-Control-Allow-Originmatches the request's origin or is set to the wildcard*, the browser passes the response data to the script. Otherwise, it throws a runtime console error.
Simple vs Preflight Requests
Browsers partition cross-origin requests into two categories based on safety characteristics:
Simple Requests
Requests that do not alter server data or use custom parameters. A request is simple if it meets all the following criteria:
- The HTTP method is
GET,HEAD, orPOST. - It only sets safe standard headers, such as
Accept,Accept-Language,Content-Language, orContent-Type. - The
Content-Typeis limited toapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain.
For simple requests, the browser sends the request immediately, verifying the Access-Control-Allow-Origin header when the response returns.
Preflight Requests
If a request could modify server state or uses custom protocols, the browser must confirm permission before sending the payload. This is triggered by:
- HTTP methods like
PUT,DELETE, orPATCH. - Custom HTTP headers (e.g.,
Authorization,X-Custom-Header). - Modern payloads like JSON (
Content-Type: application/json).
For preflight requests, the browser first issues an automatic OPTIONS request. The server must acknowledge this pre-check with the appropriate permission headers before the browser sends the actual data payload.
Preflight Mechanics and Headers
During the preflight handshake, the browser describes the intended transaction using specific metadata headers:
Access-Control-Request-Method: Declares the intended actual method (e.g.PUT).Access-Control-Request-Headers: Lists the custom headers the client plans to send (e.g.Authorization).
The server must reply with corresponding validation headers:
Access-Control-Allow-Origin: The allowed domain, or*for public resources.Access-Control-Allow-Methods: The permitted HTTP verbs (e.g.GET, POST, PUT).Access-Control-Allow-Headers: The validated custom headers.Access-Control-Max-Age: The time in seconds the browser can cache this preflight approval, reducing the latency overhead of future API calls.
Credential Sharing Rules
By default, cross-origin requests do not transmit ambient credentials like cookies, TLS client certificates, or HTTP authentication headers. To share credentials, the client must explicitly enable credential forwarding in its fetch settings (e.g. credentials: 'include').
On the server side, this requires two strict adjustments:
- The server must return
Access-Control-Allow-Credentials: true. - The server cannot use the wildcard
*for the origin header. It must return a specific, explicit origin (e.g.Access-Control-Allow-Origin: https://my-app.com).
If the server sends a wildcard origin with credentials allowed, the browser rejects the transaction, blocking access to the API payload.
Security Implications of Misconfiguration
CORS is a browser-enforced security mechanism: it does not block non-browser clients like curl, Postman, or backend proxy servers from making requests.
Common misconfigurations present severe risks:
- Reflected Origins: Writing code that dynamically copies whatever value is in the incoming
Originheader intoAccess-Control-Allow-Originwith credentials enabled. This effectively turns off SOP, allowing any malicious site on the internet to read private user data. - Wildcard IP Exposure: Allowing arbitrary internal IP or dev addresses (e.g.
http://localhost:*orhttp://*.internal) without validating port numbers, which exposes local development instances to external scripts.
A secure CORS strategy utilizes strict whitelists of verified production hostnames, denies credential access by default, and limits cached preflight permissions to reasonable intervals.
Further Reading
- MDN Web Docs: Cross-Origin Resource Sharing (CORS) — Detailed explanation of browser CORS mechanics and header configurations.
- OWASP Cross-Origin Resource Sharing Cheat Sheet — Practical guides for preventing CORS vulnerabilities and implementing secure origin validation.
- W3C Same-Origin Policy Specification — Foundations of the Same-Origin Policy mechanism inside modern user agents.
Prerequisites
Code Examples
Core Literature References
Cross-Origin Resource Sharing (CORS) W3C Recommendation
by Anne van Kesteren — Introduction and Resource Sharing Algorithms
View sourceContinue learning
ACID & Isolation Levels
Deep dive into database transaction guarantees, isolation levels, concurrency anomalies like write skew, and control mechanisms such as MVCC, 2PL, and SSI.
API Gateways
Understand the API Gateway pattern as the central ingress point for microservices, handling routing, auth, rate limiting, and protocol translation.
API Security & OAuth 2.0
Understand API authentication and authorization mechanisms, JWT security, and the OAuth 2.0 framework including Authorization Code Flow with PKCE.