# CORS controls browser reads, not API authorization

> Understand preflight and credential behavior, then enforce authentication, object-level authorization, CSRF defenses, and input validation on the server.

Canonical URL: https://www.devobs.io/articles/cors-is-not-api-authorization/
By: Ines Costa
Published: 2024-06-16T03:01:48.679Z
Updated: 2026-09-05
Section: Architecture

CORS tells a browser whether JavaScript from one origin may read a cross-origin response. It does not stop curl, a mobile app, a backend service, or malicious software from sending the same HTTP request. Every sensitive endpoint still needs server-side authentication and authorization.

## What the browser enforces

The [Fetch Standard](https://fetch.spec.whatwg.org/) defines CORS as part of the browser’s fetch processing. For some cross-origin requests, the browser first sends a preflight with the intended method and headers. The server’s response tells the browser which origins, methods, headers, and credential modes may proceed.

Other requests are “simple” enough to be sent without preflight. The browser may still prevent the calling script from reading the response. That distinction matters: a state-changing form-style POST can reach the server even when its response is opaque to the attacker.

A CORS allowlist is therefore a user-agent policy. Non-browser clients do not owe your server a preflight and can set an Origin header of their choice. The API must make the same access decision regardless of whether the caller is JavaScript, a command-line tool, or another service.

## Credentials change the browser risk

Cookies can accompany cross-site requests according to cookie attributes and fetch credential mode. Allowing credentials in CORS requires a specific allowed origin rather than a wildcard. Reflecting an arbitrary Origin value with credential support effectively grants any requesting site permission to read authenticated responses.

CORS also does not replace cross-site request forgery defenses. If a browser automatically attaches credentials to a state-changing request, validate an anti-CSRF token or another request property bound to the legitimate application, and configure cookies deliberately.

Bearer-token APIs avoid ambient cookie attachment, but the token still needs issuer, audience, signature, expiry, and scope validation. A leaked token is not made safe because the attacker’s website is absent from the CORS list.

## Enforce the resource decision

The [OWASP Authorization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) recommends validating permissions on every request, regardless of whether it came from an AJAX script, server-side code, or another source. Translate that into an endpoint sequence:

1. Authenticate the credential and bind it to a tenant.
2. Parse and validate the requested action and resource identifier.
3. Load the resource within that tenant boundary.
4. Authorize the subject’s action on that resource.
5. Perform the mutation atomically with relevant state preconditions.
6. Record a security-relevant denial without leaking protected details.

Object-level checks matter. Verifying that a caller has the “editor” role does not prove they may edit document 42. Test another user’s identifiers and another tenant’s identifiers even when they are hard to guess.

## Demonstrate the boundary

Create an endpoint that increments a counter. From an excluded web origin, send a simple POST and observe that the server may receive it while the browser hides the response. Send the same request from curl and observe that CORS is irrelevant. Then add server authorization and verify both clients receive a denial without the required permission.

Keep CORS configuration narrow because it limits which sites can expose responses to browser scripts. Include only required origins, methods, and headers; avoid origin reflection; add Vary: Origin when responses differ by origin; and test credentialed requests.

Also test error responses. If successful responses have a careful origin policy but validation or stack-trace responses use a wildcard, a malicious site may read sensitive failure details. Apply CORS headers through one reviewed response layer, and verify OPTIONS handling does not bypass rate limits or authentication on the real operation.

Review one sensitive endpoint with a non-browser client today. If removing the Origin header changes authorization, the design is relying on the wrong boundary. Move the access decision into the endpoint and keep CORS as a separate browser data-sharing policy.

## Source references

- <https://fetch.spec.whatwg.org/>
- <https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html>
