Backend

CORS Explained for Backend Developers

Learn how CORS works for backend APIs, including origins, preflight requests, credentials, allowed headers, cookies, security limits, and common mistakes.

CORS stands for Cross-Origin Resource Sharing. It is a browser security mechanism that controls when frontend JavaScript can read responses from a different origin.

Backend developers run into CORS when a frontend app on one origin calls an API on another origin.

Quick answer

CORS is enforced by browsers. The backend tells the browser which origins, methods, headers, and credential rules are allowed by returning CORS response headers.

If the browser decides the CORS policy does not allow the request, frontend JavaScript cannot read the response. The API may still receive the request in some cases, so CORS should not be treated as an authorization system.

Use CORS to control browser cross-origin reads. Use authentication and authorization to protect data and actions.

What is an origin?

An origin is the combination of scheme, host, and port.

These are different origins:

https://app.example.com
https://api.example.com
http://app.example.com
https://app.example.com:8443

If JavaScript running on https://app.example.com calls https://api.example.com, the browser treats that as a cross-origin request.

CORS decides whether the browser should expose the response to the calling JavaScript.

Simple requests and preflight requests

Some cross-origin requests are simple enough that the browser sends them directly.

Other requests trigger a preflight request. A preflight is an OPTIONS request that asks the server whether the real request is allowed.

Example preflight:

OPTIONS /api/orders
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type

The backend might respond:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Max-Age: 600

If the browser accepts the preflight response, it sends the real request.

Important CORS headers

Common CORS response headers include:

HeaderPurpose
Access-Control-Allow-OriginWhich origin can read the response.
Access-Control-Allow-MethodsWhich methods are allowed for preflighted requests.
Access-Control-Allow-HeadersWhich request headers are allowed.
Access-Control-Allow-CredentialsWhether credentialed browser requests are allowed.
Access-Control-Expose-HeadersWhich response headers JavaScript can read.
Access-Control-Max-AgeHow long the browser can cache preflight permission.

The most important rule is to be deliberate. Do not reflect every origin by default unless the endpoint is truly public and you understand the consequences.

Credentials and cookies

Credentialed requests are requests that include cookies, HTTP authentication, or client certificates.

For credentialed CORS requests, the backend cannot use:

Access-Control-Allow-Origin: *

It must return a specific allowed origin, and it must include:

Access-Control-Allow-Credentials: true

The frontend also needs to opt into credentials, for example with fetch:

fetch("https://api.example.com/me", {
  credentials: "include"
});

Cookie behavior also depends on attributes such as Secure and SameSite. Read CSRF vs XSS for browser credential risks around cookies and tokens.

CORS is not authentication

CORS does not prove who the caller is. It only tells the browser whether a response can be shared with JavaScript from another origin.

Non-browser clients such as curl, backend services, scripts, and mobile apps are not blocked by browser CORS enforcement in the same way.

That means sensitive APIs still need:

  • Authentication.
  • Authorization.
  • Rate limiting.
  • Input validation.
  • Auditing.
  • CSRF protection where cookie-based browser requests are involved.

Read Authorization vs Authentication for the difference between identity and permission checks.

Common API setup

A typical production API might allow:

https://app.example.com
https://admin.example.com

It might reject unknown origins, allow only expected methods, allow only necessary headers, and expose only the response headers the frontend needs.

For local development, teams often allow:

http://localhost:3000
http://localhost:5173

Keep development origins separate from production configuration so temporary local settings do not become permanent production policy.

Common mistakes

The first mistake is using Access-Control-Allow-Origin: * for private APIs. Public read-only endpoints may be fine, but private user data should be more restrictive.

The second mistake is allowing credentials for too many origins. Credentialed CORS needs a tight allowlist.

The third mistake is treating CORS errors as backend authentication failures. A request can fail CORS even when the API is otherwise healthy.

The fourth mistake is forgetting preflight handling for Authorization headers or JSON Content-Type requests.

The fifth mistake is assuming CORS protects APIs from all clients. It is a browser sharing control, not a general access-control layer.

Practical backend checklist

When configuring CORS:

  • Define allowed origins explicitly.
  • Keep local development origins separate.
  • Allow only needed methods.
  • Allow only needed request headers.
  • Expose only needed response headers.
  • Use credentials only when necessary.
  • Add Vary: Origin when responses differ by origin.
  • Keep authentication and authorization separate from CORS.

For API response behavior, read REST API Error Handling Best Practices. For status code choices around auth failures, read REST API Status Codes Explained.

Read CORS Preflight Explained for the detailed OPTIONS request flow, allowed headers, credentials, and debugging checklist. Read CSRF vs XSS, Secure HTTP Headers Explained, SameSite Cookies Explained, and Cookie Security Checklist for browser security risks around cookies and tokens.

Read Cookie vs Token Authentication before choosing browser credential storage and Authorization vs Authentication before treating CORS as an access-control mechanism. For the full sequence, browse the API Design Learning Path, Authentication Learning Path, and Backend Security Learning Path.