CORS (cross-origin resource sharing)
The browser rule that page code may only read another origin's response if that server says yes, which is why curl works and the tab does not.
See it
What it is
Browsers enforce the same-origin policy: page JavaScript cannot read a response from a different scheme, host, or port. CORS is the opt-in that relaxes it. Your API answers with Access-Control-Allow-Origin naming who may read it, and the browser hands the response to the calling page. For anything beyond a plain GET or form POST (custom headers, JSON content type, PUT or DELETE) the browser first sends a preflight OPTIONS request, and your server has to answer that separately with Allow-Methods, Allow-Headers, and optionally Max-Age so the answer is cached.
The big misconception: CORS does not protect your API. It protects users of other websites from having their logged-in data read by those sites. curl, Postman, and any server ignore it entirely, and a CORS error does not mean the request was blocked before it arrived: for simple requests it already reached your handler and ran, the browser just refused to show the response. It also does nothing about CSRF, since sending a request was never the part CORS restricted.
Gotcha: cookies and other credentials need Access-Control-Allow-Credentials: true, and that cannot be combined with the wildcard origin, so you must echo the exact caller origin. Doing that by reflecting whatever Origin arrives, with credentials on, hands every site on the internet read access to your logged-in users' data. Keep an allowlist, and set Vary: Origin so a CDN does not cache one customer's allowed origin and serve it to everyone.
Ask AI for it
Configure CORS on this API correctly and safely. Replace any wildcard or reflect-any-origin setup with an explicit allowlist of origins read from config (production domain, preview domains, localhost in development), echoing the request's Origin only when it matches the list. Set Access-Control-Allow-Credentials: true only if the browser client sends cookies, and never alongside '*'. Handle the preflight: answer OPTIONS with 204 plus Allow-Methods and Allow-Headers listing exactly what the app uses, and Access-Control-Max-Age so it is cached. Add Vary: Origin to every CORS response so caches do not mix origins. Keep real authorization checks in the handlers, since CORS is a browser control and not an access control.