What Is a CORS Error and How to Fix It in an AI-Built App
A CORS error means your browser blocked a cross-origin request. Why it happens, why the wildcard fix is risky, and a safe allow-list you can paste in.
A CORS error means your browser refused to let a web page read a response from a different origin, because the server did not say that origin is allowed. The server is usually working fine. The fix is to have your API send the right Access-Control headers for the exact site that calls it, not to switch the check off.
AI builders hit this constantly: the front end runs on one address, the API on another, and the first request fails with a red console message. Here is what the message means and how to answer it safely.
What CORS actually is
MDN defines Cross-Origin Resource Sharing as "an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources." The reason it exists is the same-origin policy: by default, a script can request resources only from the origin its page was loaded from, unless the response includes the right CORS headers. The MDN guide to CORS is the reference for everything below.
An origin is the combination of scheme, domain, and port. So http://localhost:5173 calling http://localhost:3000 is cross-origin, as is https://app.example.com calling https://api.example.com.
Two facts change how you think about it:
The browser enforces CORS, not the server. The server only sends headers. A request from curl, a script, or another server ignores CORS entirely.
The error hides detail from your code. MDN notes that CORS failures produce errors, but the specifics are not available to JavaScript. Read the browser console and the network tab instead.
Why a preflight request appears
For requests that could change data, browsers first send an OPTIONS request, called a preflight, asking the server what it permits. MDN says this applies to HTTP methods other than GET, or POST with certain content types. Sending a JSON body with a Content-Type: application/json header, or an Authorization header, is enough to trigger one.
A typical preflight asks:
OPTIONS /orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type,authorizationAnd your server has to answer with something like:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400If your API has no handler for OPTIONS, or returns an error or redirect, the real request is never sent.
The common errors and their fixes
What the console says | Likely cause | Fix |
|---|---|---|
No Access-Control-Allow-Origin header is present | The API did not send it at all | Add the header for your front-end origin |
Wildcard not allowed when credentials are included | You send cookies or auth and answer with | Return the exact origin instead of |
Preflight response was not successful | No OPTIONS handler, or it errors | Answer OPTIONS with a 2xx and the allow headers |
Request header not allowed | A header you send is not in Allow-Headers | List it, for example |
MDN's rule for credentialed requests is explicit: the server must not use the * wildcard for Access-Control-Allow-Origin, Allow-Headers, or Allow-Methods, and must give explicit values. It also notes that some browsers do not follow redirects after a preflight.
A safe allow-list to paste into your server
The tempting fix, and the one an AI assistant will often offer, is Access-Control-Allow-Origin: *. It silences the error, and for a fully public, read-only API it can be acceptable. For anything using cookies or sign-in it will not work, and for anything else it is the wrong habit. Prefer an allow-list:
const ALLOWED = new Set([
'https://app.example.com',
'http://localhost:5173',
]);
function corsHeaders(origin) {
if (!origin || !ALLOWED.has(origin)) return {};
return {
'Access-Control-Allow-Origin': origin,
'Vary': 'Origin',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
}
export async function handler(req) {
const headers = corsHeaders(req.headers.get('Origin'));
if (req.method === 'OPTIONS') {
return new Response(null, { status: 204, headers });
}
// ... your real logic here ...
return new Response(JSON.stringify({ ok: true }), {
headers: { 'Content-Type': 'application/json', ...headers },
});
}Three details matter. The origin is echoed back only when it is on your list. Vary: Origin tells caches that the response depends on who asked. And OPTIONS gets its own early return, so the preflight never reaches your business logic. Remove the localhost entry before you go live.
CORS is not security for your data
This is the point most tutorials skip. Because the browser enforces CORS, a strict policy does not stop anyone from calling your API directly. It only controls which websites' scripts can read the responses inside a visitor's browser. Your real protection is authentication and authorization on the server, and, if you use a database exposed to the browser, row level security.
The reverse also holds: a wildcard origin does not by itself expose private data to other sites, as long as your API requires credentials the other site cannot send. Treat CORS as a correctness rule, and fix real access control separately. The checks in how to review AI-generated code before you ship it and how to add rate limiting to an AI-built app cover that side.
Debugging checklist
Open the browser network tab and find the failing request. Look for a preceding OPTIONS call.
Compare the request's
Originheader with your allow-list, character by character. A trailing slash orhttpversushttpsmismatch is enough to fail.Confirm the response, not just the preflight, includes the allow-origin header.
Check that no proxy, redirect, or error page is answering before your code runs.
Hand the failing request and response headers to your AI assistant along with the allow-list code, as in how to get an AI coding agent to explain a stack trace.
FAQ
What does a CORS error mean?
The browser blocked a script from reading a response from another origin, because the response lacked the headers that permit that origin.
How do I fix a CORS error?
Have the server send Access-Control-Allow-Origin for your exact front-end origin, answer OPTIONS preflight requests, and list any custom headers in Access-Control-Allow-Headers.
Is it safe to use Access-Control-Allow-Origin: *?
It can be fine for a public API that needs no credentials. It does not work with cookies or auth headers, and an explicit allow-list is the safer default.
Why does CORS work in Postman or curl but not the browser?
Because CORS is enforced by browsers. Other tools do not apply it.
How did this land?
About the author

Developer Advocate
Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.


