Website-Pflichtencheckby Jurono
SecurityWebsiteTechnicalCode

CORS Is Not API Authorization: What the Browser Actually Protects

CORS decides whether browser JavaScript may read a cross-origin response. It does not replace authentication, role checks, CSRF protection, or server-side access control.

By Jurono
Updated: August 19, 2026

“The API is protected. We only allow CORS from our frontend domain.”

That sounds sensible, but it is dangerously incomplete.

CORS is a browser security mechanism for sharing responses across origins. The WHATWG Fetch Standard describes the protocol in exactly those terms: response headers tell the browser whether a response may be exposed to JavaScript running on another origin. That is useful, but it is not user authentication, role-based authorization, or a general firewall for your API.

The distinction matters in production. An API can prevent a hostile website from reading a response in browser JavaScript while still accepting requests from curl, backend services, mobile applications, or any other non-browser client. And some browser-generated cross-origin requests can reach the server without a CORS preflight happening first.

If CORS is treated as access control, teams often protect the wrong boundary.

Myth 1: “If the origin is not allowed, the request never reaches our API”

Not in general.

The Fetch Standard separates the HTTP request from the decision about whether the resulting response may be shared with JavaScript on another origin. For more involved cross-origin requests, a browser commonly sends an OPTIONS preflight first. Requests that remain within the CORS safelists and broadly match what traditional HTML forms can cause do not necessarily need that preflight.

So “the browser will not expose the response” is not the same statement as “the server was never contacted.”

For a public read-only endpoint, that distinction may not matter much. For a state-changing endpoint, it matters a great deal. Every API action still needs server-side controls: Is the user authenticated? May this user read or change this specific resource? Is this method valid for the endpoint? Is the request adequately protected against cross-site actions? Does the object belong to the current tenant, account, or workspace?

CORS cannot answer any of those questions.

Myth 2: “The preflight is our security check”

A preflight is a browser compatibility check for the CORS protocol, not business authorization.

The browser sends an OPTIONS request announcing the intended method and, when relevant, non-safelisted request headers. The server can answer with Access-Control-Allow-Methods and Access-Control-Allow-Headers to state what the CORS policy supports.

One operational detail is easy to miss: according to the Fetch Standard, CORS preflight requests do not include credentials. If authentication middleware requires a valid session or bearer token for every request including OPTIONS, it can reject legitimate preflights before your CORS layer has a chance to answer.

A cleaner model is:

  1. Preflight answers the browser question: “May this frontend attempt this request under the CORS protocol and receive the response?”
  2. The actual API request answers the security question: “Is this caller authenticated and authorized to perform this action?”

Those layers can reinforce each other. They should not be confused.

Myth 3: “Access-Control-Allow-Origin: * makes a private API public”

For a genuinely private API, * is a serious red flag — but precision still matters.

The Fetch Standard presents Access-Control-Allow-Origin: * as appropriate for resources that are genuinely public. Its practical rule is clear: if a resource can safely be retrieved by an arbitrary device using tools such as curl or wget, exposing it to arbitrary browser origins can be reasonable.

Credentialed CORS requests have stricter rules. With credentials: include, Access-Control-Allow-Origin cannot be *; the response needs a concrete origin and Access-Control-Allow-Credentials: true.

But a concrete origin is not automatically safe. A dangerous implementation can read the incoming Origin, reflect it into Access-Control-Allow-Origin without validation, and also send Access-Control-Allow-Credentials: true. There is no meaningful origin boundary left even though no wildcard appears.

Dynamic origins should therefore be checked against an explicit, exact allowlist. Avoid substring matching such as “contains example.com,” ambiguous suffix logic, or blind reflection.

Myth 4: “Our frontend domain is allowed, so that is enough”

An origin is not a user.

https://app.example.com describes a scheme, host, and port context. It does not tell your API which person is making the request, which role they have, which tenant they belong to, whether the account has been suspended, or whether they may read document 4711.

CORS answers a browser-sharing question between origins. Business authorization remains server-side. This is especially important in multi-tenant SaaS: a perfect CORS allowlist will not stop user A from reading user B's data if the API fails to enforce object- and tenant-level permissions.

Origin-based sharing and object-level authorization are completely different controls.

Myth 5: “CORS automatically protects us from CSRF”

It does not.

Cross-Site Request Forgery exists because browsers can, under certain conditions, be induced to send requests in the context of an authenticated user. Whether cookies are actually included depends on cookie scope, SameSite, request shape, browser policy, and other details. CORS alone is not the statement “this state-changing operation may only come from our own UI.”

The Fetch Standard explicitly makes preflight necessary only for requests that go beyond what traditional HTML-form-like requests can do. The W3C Fetch Metadata draft addresses the underlying problem directly: browsers can be induced to make requests to exposed endpoints and may include ambient credentials, so servers need additional context and policy decisions.

Depending on the architecture, protection for sensitive actions can combine intentionally scoped SameSite cookies, CSRF tokens for cookie-based sessions where required, no state-changing behavior on GET, server-side Origin/Referer or Fetch Metadata checks as defense in depth, re-authorization for sensitive actions, and independent business permission checks.

CORS is one layer in that system, not the entire wall.

Red flag: one global “make CORS work” middleware

Many CORS failures are not one incorrect header. They come from a global middleware that emits the same policy for every route.

A public asset, a public API, and an authenticated account endpoint have different requirements. Yet common patterns include setting Access-Control-Allow-Origin globally, reflecting every request Origin, enabling Access-Control-Allow-Credentials: true everywhere, routing OPTIONS through the same authentication and business logic as normal API calls, and maintaining one ever-growing allowlist for local development, previews, and production.

A small matrix by API class is clearer:

Endpoint classCross-origin needed?Credentials?Allowed origins
Public non-personalized datamaybenopossibly *
Browser app with a sessionyesmaybeexplicit app origins
Internal admin APIpreferably no or tightly scopedyesexplicit admin origins
Server-to-server APICORS is irrelevantAPI authenticationno browser sharing required

Now CORS becomes an intentional interface decision rather than global decoration.

Do not forget Vary: Origin

When a server dynamically returns different Access-Control-Allow-Origin values based on the incoming Origin, caches become part of the reliability and security model.

The Fetch Standard calls for Vary: Origin in this situation. Without it, a cache can reuse a response created for another origin. The result may be intermittent and confusing: an allowed frontend suddenly receives a cached response without the right CORS header, or behavior changes depending on which requester populated the cache first.

If Access-Control-Allow-Origin is always * or always the same fixed origin, the response can be consistent without varying on Origin. CDN, reverse proxy, and API cache behavior therefore belong in the review.

A useful CORS audit tests more than the happy path

1. Browser from an allowed origin

Exercise real application flows while signed out, signed in, and with an expired session; with custom headers; with GET, POST, PUT/PATCH, and DELETE; and with error responses. 401, 403, 404, and 500 responses need coherent CORS behavior too, or the frontend sees an opaque network failure instead of the real API error.

2. Browser from a disallowed origin

Run equivalent requests from a different origin. Do not inspect only the console; inspect server logs as well. Was a preflight sent? Was the actual request sent? Did the server execute an action? Was the response merely withheld from JavaScript?

3. A non-browser HTTP client

Call the same endpoint with a normal HTTP client. If the only protection was “this origin is not allowed,” the problem becomes obvious. A private API must enforce authentication and authorization equally well without a browser present.

4. The real cache and infrastructure path

Test through the production chain Browser → CDN/WAF → reverse proxy → API. Look for duplicate or contradictory CORS headers, missing Vary: Origin, incorrect OPTIONS handling, redirects, and different behavior between success and error paths.

Fetch Metadata can add request context

The W3C Fetch Metadata draft defines request headers including Sec-Fetch-Site, Sec-Fetch-Mode, and Sec-Fetch-Dest. They give a server additional information about how a browser initiated a request. A server can, for example, detect that a request is cross-site, or that an endpoint intended for an API call is unexpectedly being requested as an image.

That can be a useful extra defense against classes of cross-site request behavior. But context still is not user authorization. Fetch Metadata can complement authentication, authorization, and CSRF controls; it does not replace them.

What Website-Pflichtencheck would inspect

A CORS and API-boundary review should not stop at checking whether Access-Control-Allow-Origin exists. A technical review can inspect public, authenticated, and administrative API classes, the origins actually allowed, dynamic origin matching, credential behavior, preflight responses and OPTIONS routing, CORS headers on success and error responses, Vary: Origin and CDN/proxy caching, behavior from a deliberately disallowed browser origin, behavior from a non-browser HTTP client, CSRF and cookie controls on state-changing actions, Fetch Metadata as an additional browser-context signal, and most importantly server-side authentication plus role, tenant, and object permissions.

The important question is not: “Is CORS enabled?”

It is: “Which boundary is CORS meant to protect here, and which boundaries must still be enforced independently by the server?”

Once that answer is explicit, CORS becomes much simpler. It no longer has to pretend to be authentication, authorization, CSRF protection, and a network firewall at the same time.

And it never should.

Jurono logo

Jurono

Technical website audits, website fixes, and AI code rescue for small businesses, practices, law firms, and founders in Germany.

Get our free security checklist before you go.

Download free PDF

Want a first signal in 30 seconds? Run the free website quick test.

Get website notes by email

One short technical note every two weeks. No spam, no sales pitch.

Matching offers

Move forward directly

Based on the topics in this article — without a long search.

Manual Website Check

When nobody is sure which scripts, cookie signals, or technical risks are currently running on the site.

249

Manual technical first assessment and clear priorities within two business days.

  • Quickly see whether tracking, cookies, external services, or HTTPS look suspicious
  • Mobile, load time, and technical issues explained in plain language
  • The most important points in a short priority list
Continue with Manual Website Check

Technical Website Audit

When the website matters, but nobody knows which visible required areas, technical risks, and fixes actually have priority.

549

Audit, assessment, and concrete action plan within 3-5 business days.

  • Everything from the manual website check, assessed and documented in more depth
  • Concrete findings for cookie, tracking, and external service signals
  • Visible required areas checked technically, without legal advice
Start Technical Website Audit

AI Code Triage

When the project starts, but nobody knows why it keeps breaking.

390

Code review, build/import check, and rescue plan within two business days.

  • Repository check for broken imports, missing packages, and build errors
  • Assessment: repair, restructure, or discard
  • Prioritized fix list with effort estimate
Get clarity with AI Code Triage

Get clarity before you commit to fixes.

Start with a technical check. If the findings are minor, you can stop there, hand the report to your existing team, or book targeted fixes later.

Technical audit and implementation, not legal advice. I check visible signals, integrations, and delivery issues; legal texts and binding legal assessments remain the work of lawyers or privacy consultants.

CORS Is Not API Authorization: What the Browser Actually Protects