CORS: Cross-Origin Resource Sharing

CORS is a security feature that restricts web pages from making requests to a different domain.

Cross-Origin Resource Sharing

CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls whether a webpage can request resources from a different domain. Understanding CORS is essential for building APIs and modern web applications, and for diagnosing one of the most common errors frontend developers encounter.

What Is CORS

Browsers enforce a security rule called the Same-Origin Policy. This policy prevents JavaScript running on one domain from making requests to a different domain. Without it, a malicious website could silently make requests to your bank, your email provider, or any other service where you are logged in, using your existing session cookies.

CORS is a controlled relaxation of the Same-Origin Policy. It allows servers to explicitly declare which outside origins are permitted to access their resources. When a browser sees a cross-origin request, it checks whether the server has granted permission via CORS headers before allowing the response to reach the JavaScript code that requested it.

Two URLs are considered the same origin only if they share the same scheme, domain, and port. A difference in any one of those three parts makes the request cross-origin, regardless of how similar the URLs look.

Origin AOrigin BSame Origin?Reason
https://example.comhttps://example.com/tutorialYesSame scheme, domain, and port. Path does not affect origin.
https://example.comhttp://example.comNoDifferent scheme (https vs http)
https://example.comhttps://api.example.comNoDifferent subdomain counts as a different origin
https://example.comhttps://example.com:8080NoDifferent port number
https://example.comhttps://example.orgNoDifferent domain entirely

How CORS Works

When JavaScript makes a cross-origin request, the browser does not simply send it and let the server decide whether to respond. Instead, the browser itself acts as a gatekeeper. It checks the server's CORS headers and blocks the response from reaching JavaScript if the current origin is not permitted.

For requests that could cause side effects on the server, such as POST requests with a JSON body or requests with custom headers, the browser first sends a preflight request using the OPTIONS method. This lightweight request asks the server what it allows before committing to the actual request.

  1. JavaScript on your page tries to call an API on a different domain
  2. If the request is non-simple, the browser sends a preflight OPTIONS request to the API first
  3. The server responds to the preflight with CORS headers declaring what origins, methods, and headers it permits
  4. If the browser sees that the current origin is allowed, it sends the actual request
  5. The server processes the actual request and responds with the data plus CORS headers again
  6. If the origin is not allowed at any stage, the browser blocks the response and throws a CORS error in the console

Simple requests, which are GET or POST requests with only standard headers and a content type of application/x-www-form-urlencoded, multipart/form-data, or text/plain, skip the preflight step and go directly to the actual request. However, the browser still checks the CORS headers on the response before passing it to JavaScript.

CORS Response Headers

CORS is controlled entirely through HTTP response headers that the server includes in its replies. The browser reads these headers and uses them to decide whether to grant or block access to the response.

HeaderWhat It ControlsExample Value
Access-Control-Allow-OriginWhich origin or origins are permitted to access the resourcehttps://example.com or * for all origins
Access-Control-Allow-MethodsWhich HTTP methods the browser is permitted to useGET, POST, PUT, DELETE
Access-Control-Allow-HeadersWhich request headers the browser is allowed to sendContent-Type, Authorization
Access-Control-Allow-CredentialsWhether cookies and authentication headers are included in cross-origin requeststrue
Access-Control-Max-AgeHow long the browser can cache the preflight response before sending another86400 (24 hours)
Access-Control-Expose-HeadersWhich response headers JavaScript is allowed to read beyond the default safe listX-Custom-Header

Common CORS Scenarios

CORS errors are among the most frequently encountered issues in frontend development. Most of them follow one of a few common patterns, each with a straightforward fix on the server side.

  • React app on localhost:3000 calling a Node API on localhost:5000: Different ports make these cross-origin even though both are on localhost. Add a CORS middleware package to the API server and configure it to allow the frontend origin during development.
  • Frontend on example.com calling api.example.com: Different subdomains are cross-origin. The API must explicitly set Access-Control-Allow-Origin: https://example.com or include it in a dynamic allowlist.
  • Any site consuming a public third-party API: The API operator must allow your origin or use Access-Control-Allow-Origin: *. You cannot fix CORS errors on APIs you do not control from the client side.
  • Sending cookies or authentication headers cross-origin: You must set Access-Control-Allow-Credentials: true on the server and use the exact origin rather than a wildcard. The fetch call must also include credentials: 'include'.
Allow a specific origin with credentials (Node.js / Express example):
app.use((req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", "https://example.com");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
  res.setHeader("Access-Control-Allow-Credentials", "true");
  if (req.method === "OPTIONS") return res.sendStatus(204);
  next();
});
Allow all origins for a public API (PHP example):
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");

What Triggers a Preflight Request

Not every cross-origin request triggers a preflight. The browser only sends a preflight OPTIONS request when the actual request meets certain conditions that make it potentially unsafe to send without prior permission.

ConditionTriggers Preflight?
GET or HEAD with only simple headersNo
POST with application/x-www-form-urlencoded or text/plainNo
POST with application/json bodyYes
PUT, PATCH, or DELETE requestsYes
Any request with an Authorization headerYes
Any request with a custom headerYes

Frequently Asked Questions

  1. Is CORS a server-side or browser-side issue?
    CORS is enforced by the browser, not by the server or the network. The server's only role is to include the correct response headers declaring what it permits. Requests made directly from server-to-server code, command-line tools, or Postman are never subject to CORS restrictions because those tools do not enforce the Same-Origin Policy. Only browsers do.
  2. Does a wildcard origin work with credentials?
    No. If you set Access-Control-Allow-Credentials: true, you must specify the exact allowed origin in Access-Control-Allow-Origin rather than using a wildcard. A wildcard combined with credentials is explicitly disallowed by the CORS specification, and browsers will block the request even if the server sends both headers together.
  3. Why do I get a CORS error in the browser but not in Postman?
    Postman does not enforce the Same-Origin Policy because it is not a browser. CORS is a browser security feature, not an HTTP protocol rule. The server is responding perfectly fine in both cases, but the browser inspects the CORS headers on the response and blocks your JavaScript from accessing it if the origin is not permitted. Postman applies no such restriction.
  4. Can I fix a CORS error from the frontend without changing the server?
    Generally no, if you do not control the server. The only reliable fix is having the server send the correct Access-Control-Allow-Origin header. One workaround during development is to proxy requests through your own backend, which forwards them to the target API server-to-server without triggering browser CORS checks. Some frameworks like Create React App and Vite have built-in proxy configuration for this purpose.
  5. What is the difference between a simple request and a preflighted request?
    A simple request is one the browser considers low-risk enough to send directly without asking permission first. These are typically GET requests or basic POST requests with standard form content types. A preflighted request involves methods, headers, or content types that could cause data modifications, so the browser sends a preliminary OPTIONS request to confirm the server is willing to accept it before sending the real request.

Conclusion

CORS exists to protect users from malicious cross-origin JavaScript requests while still allowing legitimate cross-origin communication between trusted origins. For API developers, setting the correct CORS headers is a fundamental configuration task that determines whether your frontend can communicate with your backend at all. Understanding which requests trigger preflights, how credentials interact with origin headers, and why browsers enforce rules that other tools do not will save you significant debugging time. To build a complete picture, explore HTTP headers, REST APIs, and HTTPS alongside your understanding of CORS.