Conceptly
← All Concepts
πŸ›‘οΈ

CORS

SecurityA browser security mechanism that controls requests for resources from different origins

CORS (Cross-Origin Resource Sharing) is an HTTP-header-based mechanism that controls access to resources on one origin from a page loaded from a different origin, allowing the browser to pass through a response only when the server has explicitly permitted it. An origin is the combination of protocol, host, and port; if any one of these three differs, the request is considered cross-origin.

β–ΆArchitecture Diagram

πŸ”„ Process

Dashed line animations indicate the flow direction of data or requests

Why do you need it?

When calling an API from JavaScript in the browser, a request that worked fine against the same server gets blocked by the browser the moment the API server is on a different domain. A red error appears in the developer tools, the server logs show a normal response, but the browser refuses to deliver it. This is not a bug -- it is the browser's Same-Origin Policy deliberately blocking the request. This policy protects the cookies and session of a site the user is logged into from being freely used by scripts on another site. But in the modern web, the frontend and API being deployed on different domains, loading fonts from a CDN, and calling third-party services are all everyday occurrences. The Same-Origin Policy alone ends up blocking even legitimate requests. CORS is the standard way for a server to tell the browser that requests from a particular origin are acceptable.

Why did this approach emerge?

In the early web, it was common for a browser to receive HTML, CSS, JavaScript, and data all from a single server. The Same-Origin Policy was a natural security boundary in that environment, built on the implicit assumption that there was no need to fetch resources from a different server via JavaScript. But as AJAX became ubiquitous and frontends evolved into standalone applications, that assumption collapsed. Separate API servers, static files served from CDNs, and external service calls became the standard architecture. Workarounds such as JSONP emerged but had security holes, and there was a need for a standard way for servers to explicitly declare which origins they were willing to respond to. When the W3C finalized CORS as a recommendation in 2014, the goal was to preserve the security of the Same-Origin Policy while safely enabling legitimate cross-origin communication.

How does it work inside?

The core of CORS is an HTTP-header negotiation between the browser and the server. When the browser sends a cross-origin request, it first checks whether the request qualifies as a 'simple request'. If it is a GET or POST without special headers, the browser sends the request directly and examines the Access-Control-Allow-Origin header in the response. If that header includes the current origin, the response is passed to JavaScript; if not, the response is discarded. If the request does not meet the simple request criteria, the browser first sends a preflight request using the OPTIONS method before the actual request. It is asking the server: 'Is this method and these headers allowed?' The server responds with Access-Control-Allow-Methods and Access-Control-Allow-Headers specifying what is permitted, and only then does the browser execute the actual request. To include credentials such as cookies or an Authorization header, the server must additionally respond with Access-Control-Allow-Credentials: true, and in that case the wildcard (*) cannot be used in Allow-Origin -- a specific origin must be named. This constraint reflects a deliberate design choice to exercise stricter control over requests that include credentials.

In Code

Read allowed scope from the preflight response

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type

Read the OPTIONS response first: it tells you which origin, methods, and headers the browser is allowed to use before the real request proceeds.

Credentialed requests cannot be opened with a wildcard

fetch("https://api.example.com/me", {
  credentials: "include",
  headers: {
    "Content-Type": "application/json",
  },
});

Once credentials are included, cookies travel with the request, so the server must explicitly allow a specific origin instead of relying on a wildcard.

What is it often confused with?

CORS and CSP are both security mechanisms that operate in the browser and are easy to confuse, but the direction of protection is different. CORS controls whether the server permits JavaScript-initiated requests from a different origin to that server. The resource being protected is the server's data, and the policy decision belongs to the responding server. CSP controls which origins' scripts, images, and styles may be loaded within this page. The resource being protected is the user viewing the page, and the policy decision belongs to the server serving the page. In summary: CORS is a structure in which the other server grants permission for outgoing requests, while CSP is a structure in which your own server pre-defines the permitted range for incoming resources. If a frontend API call is being blocked, CORS is the likely culprit. If a script inserted into the page is not executing, CSP policy should be the first suspect.

When should you use it?

In architectures where the frontend and API server are deployed separately, configuring CORS is a practical task that begins on day one of deployment. If the frontend is at localhost:3000 and the API is at localhost:8080 in a local development environment, those are already different origins, and API calls will fail without CORS configuration. The most common mistake is setting Access-Control-Allow-Origin: * to open all origins for convenience during development. This is acceptable in development, but opening credentials-bearing requests to a wildcard in production can expose the application to CSRF attacks. In production, it is standard practice to name the permitted origins explicitly and to set Access-Control-Max-Age on preflight responses to reduce unnecessary repeated OPTIONS requests. The reason that modifying frontend code cannot fix a CORS error is that CORS policy is determined by the response headers from the server. The starting point for debugging is examining the preflight response headers in the browser's Network tab.

Separating frontend and API server -- configuring an allow policy when an SPA requests data from an API server on a different domainLoading fonts and images from a CDN -- safely using resources hosted on an external CDN within the pageBrowser communication between microservices -- allowing API calls between services deployed on different subdomainsThird-party widget integration -- enabling payment, map, and analytics widgets to exchange data with the host page