Your AI coding agent calls your API from the browser, and CORS blocks it even though the API works

Effort About 30 min Code edits

AI-built frontends call a separate API and the browser blocks the response with a CORS error. Here is the mechanism from MDN, why JSON requests trigger a preflight, and the server-side headers that fix it.

Jump to the fix
On this page

Your AI coding agent built a frontend on localhost:3000 and a separate API on localhost:8000. The request works in curl and in Postman. In the browser the console shows a CORS error, the response is blocked, and your code only sees a generic failed fetch.

The instinct is to change the fetch call. The fix is almost always on the server.

Why this happens

CORS is enforced by the browser, not by your API. MDN defines it as an HTTP-header based mechanism that lets a server indicate origins other than its own from which a browser should permit loading resources. An origin is the scheme, domain and port together, so http://localhost:3000 and http://localhost:8000 are different origins. That is why curl never complains: it is not a browser and does not check.

Two details trip up agent-written code:

  • Your code cannot see the reason. MDN notes that for security, specifics about a CORS error are not available to JavaScript. The only place to read what went wrong is the browser console.
  • JSON requests get a preflight. Only a short list of requests skip it. MDN’s list of what a “simple request” may use for Content-Type is application/x-www-form-urlencoded, multipart/form-data and text/plain. A fetch with Content-Type: application/json, or with an Authorization header, falls outside that, so the browser first sends an OPTIONS request and only continues if the server answers it correctly. Agents often add a handler for GET and POST and forget that OPTIONS exists.

How to tell if this is your problem

  1. Open the console and the Network tab. Look for the blocked request. If you see an OPTIONS request to the same URL before it, the preflight is involved.
  2. Check the response headers on that OPTIONS request. If Access-Control-Allow-Origin is missing, the server never opted in.
  3. Check whether you send cookies. If your fetch uses credentials: "include" and the server sends Access-Control-Allow-Origin: *, the browser blocks it. MDN says the server must not use the wildcard for a credentialed request and must name an explicit origin.

The fix

Make the API answer the preflight and name your frontend’s origin. The headers MDN’s example uses for a preflight response look like this:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
  • Access-Control-Allow-Origin is the exact origin of your frontend.
  • Access-Control-Allow-Methods lists the methods the real request may use.
  • Access-Control-Allow-Headers lists the request headers you send, such as Content-Type and Authorization.
  • Access-Control-Max-Age controls how long the browser caches the preflight result. MDN says the default is 5 seconds, and each browser caps the value.

Most frameworks do this for you through a CORS middleware (Express has the cors package, FastAPI has CORSMiddleware). Configure it with your frontend origin rather than letting the agent paste in a wildcard.

If you send cookies

Use credentials: "include" in fetch, and have the server send Access-Control-Allow-Credentials: true together with an explicit origin, not *. MDN says that without that header the browser rejects the response and does not hand it to your code. The preflight response needs the same header, because preflight requests never carry credentials themselves.

If the origin changes per request

If your server picks the origin from an allowlist, MDN says it should also send Vary: Origin, so caches know the response differs by the Origin request header.

Do not “fix” it with a wildcard in production

Access-Control-Allow-Origin: * makes the error disappear for public, cookie-less APIs. It is the wrong answer for anything that uses cookies, and it is the shortcut agents reach for first. Allow the specific origins you own.

How to avoid this next time

Tell the agent up front: “The frontend and API run on different origins. Add CORS middleware on the API that allows only these origins: <your frontend URLs>. Handle OPTIONS preflight, allow the Content-Type and Authorization headers, and never use * when cookies are involved.” In review, open the Network tab, find the OPTIONS request and confirm the response carries the three Access-Control-Allow-* headers.

Comments