CORS Protects the Browser User, Not the API Server: The Reflected-Origin Hole Is Worse Than `*`
Credentials-bearing endpoints configured with a reflected Origin are effectively open to any website; an attacker page can replay a bank transfer with the victim's cookies. A two-line CORS fix becomes a CSRF vector unless the team treats allowlisting as a security decision.
Without CORS, a page on baidu.com could POST to bank.com with the user's bank login cookie and transfer money. That request reaches the server and executes; the browser only blocks the response. CORS is a read-protection layer for the person in front of the browser, not a server-side authentication gate.
`Access-Control-Allow-Origin: *` is fine for public, credential-free endpoints, but browsers refuse to attach cookies when the value is `*`. A supposedly convenient backend pattern reflects the incoming Origin verbatim while setting credentials true, which defeats the browser's safeguard and exposes authenticated APIs to every site. A production CORS setup for Cloudflare Workers uses an origin allowlist, handles OPTIONS preflight, sets Access-Control-Max-Age to 86400, and adds Vary: Origin to keep cached responses safe.
CORS does not stop cross-origin writes. Native form submissions, img, script, and link tags bypass its restrictions, so forms enable classic CSRF and image tags can fire GET URLs with user cookies. GET endpoints must never mutate state, and origin or referer checks belong on every state-changing request.
Asking the backend to add CORS is usually treated as routine config, but for authenticated APIs it is a security boundary decision; the right follow-up is whether the endpoint requires a login session.
Reflecting Origin can be more dangerous than `*` because it sidesteps the browser's built-in wildcard-with-credentials restriction, turning CORS into the exact vulnerability it exists to prevent.
Since CORS only blocks response reading, a backend's trust boundary is incomplete: any GET endpoint with side effects is remotely triggerable by an image tag.
The common split between simple and preflighted requests means a denied response can still be fully processed by the server, so CORS gives no assurance that a request wasn't executed.