Guides / Prompting and shipping

How to Fix CORS Errors (and Why They Happen)

What CORS errors mean, why browsers block cross-origin requests, and how to fix them on the server, with preflight and credentials, or with a same-origin API.

Mythex Team · 2026-09-29 · 6 min read

A CORS error means your web page tried to read a response from a different origin (another domain, port or protocol), and that server didn't send headers saying it's allowed. Browsers block the response to protect users. You fix it on the server, by returning an Access-Control-Allow-Origin header for your site's origin and handling OPTIONS preflight requests, or you avoid it by sending the request through your own backend on the same origin.

It's one of the most confusing errors in web development because the request often reaches the server fine. This guide explains what's happening and how to fix each variant.

What "origin" means

An origin is the combination of protocol, domain and port:

URLOrigin
https://myapp.com/dashboardhttps://myapp.com
https://api.myapp.com/usershttps://api.myapp.com (different subdomain, so different origin)
http://localhost:5173http://localhost:5173
http://localhost:3000http://localhost:3000 (different port, so different origin)

Browsers follow the same-origin policy: JavaScript on a page can freely read responses from its own origin, but not from others. CORS (Cross-Origin Resource Sharing) is the system that lets a server opt in: "responses from me may be read by pages from these origins."

Two consequences surprise people:

  • The request often reaches the server. CORS usually blocks your code from reading the response, not the server from receiving the request. A POST may have been saved even though you saw an error.
  • Only browsers enforce it. curl, Postman and server-side code ignore CORS entirely. That's why "it works in Postman" tells you nothing about CORS.

Step 1: Read the exact error

Open developer tools (right-click → Inspect), go to the Console, and read the full message. Chrome's wording tells you which problem you have:

Message containsWhat's wrong
No 'Access-Control-Allow-Origin' header is presentThe server didn't send the header at all, or the request failed before reaching your CORS code (for example a 404 or 500)
The 'Access-Control-Allow-Origin' header has a value '...' that is not equal to the supplied originThe server allows a different origin than yours
Response to preflight request doesn't pass access control checkThe OPTIONS preflight failed
...must not be the wildcard '*' when the request's credentials mode is 'include'You're sending cookies, and the server replied with *
Request header field authorization is not allowedThe server didn't list that header in Access-Control-Allow-Headers

Then open the Network tab, find the failing request (and any OPTIONS request before it), and look at its status code and response headers.

Check the status code first. Many "CORS errors" are really a server error in disguise. If the server crashes or returns a 404, its error response often lacks CORS headers, so the browser reports CORS instead of the real problem. Fix the 500 or 404, and the CORS error may disappear.

Step 2: Decide whether you should call that API from the browser at all

Before adding headers, ask: should the browser be calling this API directly?

  • A third-party API that needs a secret key (OpenAI, Stripe secret key, most email services) should never be called from the browser. The key would be visible to every visitor. Call it from your backend instead, and the CORS problem disappears, because server-to-server requests aren't subject to CORS. See how to keep API keys safe.
  • A third-party API you don't control that doesn't send CORS headers can't be fixed from your side. Call it from your backend.
  • Your own API can be fixed by configuring it properly. Continue below.

Step 3: Fix it on your server

The server must add the right headers to its responses, including error responses.

For the actual request, send:

  • Access-Control-Allow-Origin: https://myapp.com (your frontend's exact origin, no trailing slash)
  • Vary: Origin, if the value depends on which origin asked

For the preflight OPTIONS request, respond with a success status (200 or 204) and:

  • Access-Control-Allow-Origin: https://myapp.com
  • Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
  • Access-Control-Allow-Headers: Content-Type, Authorization (every custom header you send)
  • Optionally Access-Control-Max-Age: 600, so the browser caches the preflight for a while

Most frameworks have a package or setting that does this. In Express, the cors package; in FastAPI, CORSMiddleware; in ASP.NET Core, AddCors; in Spring, @CrossOrigin or a CORS configuration. A typical Express setup:

import cors from "cors";

app.use(cors({
  origin: ["https://myapp.com", "http://localhost:5173"],
  credentials: true,
}));

Put CORS handling before your routes and authentication, so preflight requests (which carry no login) aren't rejected with a 401.

With an AI builder, describe the setup precisely:

The frontend at https://myapp.com calls the API at https://api.myapp.com and gets "Response to preflight request doesn't pass access control check". Configure CORS on the API to allow exactly https://myapp.com and the preview origin, allow GET, POST, PATCH, DELETE with Content-Type and Authorization headers, handle OPTIONS before auth, and include CORS headers on error responses too.

Step 4: Handle cookies and credentials

If your frontend sends cookies (for example, a login session) to another origin, both sides must agree:

  • The frontend sets credentials: "include" on fetch, or withCredentials: true in axios.
  • The server sends Access-Control-Allow-Credentials: true.
  • The server's Access-Control-Allow-Origin must be the exact origin, not *.

Cross-site cookies also need SameSite=None; Secure on the cookie, and some browsers restrict third-party cookies further. That's one more reason to prefer the approach in Step 5.

Step 5: Avoid CORS with a same-origin setup

Often the cleanest fix is to not make cross-origin requests at all. Serve the API from the same origin as the page, for example at https://myapp.com/api/...:

  • In development, most dev servers can proxy. Vite's server.proxy setting can forward /api to your backend on another port, so the browser only ever talks to one origin.
  • In production, route /api to the backend through your host's rewrite or proxy settings, or serve the frontend and API from one server.

The frontend then calls a relative path like fetch("/api/orders"). There's no cross-origin request, no preflight across origins, and no cookie headaches. It also means you don't hard-code an API domain that differs between preview and production.

Change the frontend to call the backend with relative /api paths instead of a full URL, and set up a dev proxy so /api forwards to the API server. Then remove any CORS settings we no longer need.

What not to do

  • mode: "no-cors" in fetch. It doesn't bypass anything; it gives your code an empty, unreadable response.
  • Browser extensions that disable CORS. They only change your browser. Every visitor still sees the error.
  • Public "CORS proxy" services for real apps. They can read and change everything passing through them, and they go down.
  • Access-Control-Allow-Origin: * on an API that uses logins. It doesn't work with credentials anyway, and it's the wrong default for private data.
  • Reflecting any Origin header back with credentials allowed. That lets any website make logged-in requests on your users' behalf. Use an allowlist.

Common causes in AI-built apps

SituationFix
Frontend calls a third-party API with a secret keyMove the call to the backend
Works in preview, fails on the published domainAdd the published and custom-domain origins to the allowlist, or use relative /api paths
Error only on POST, PUT or DELETEPreflight not handled; add OPTIONS handling and allowed headers
Error appeared after adding loginCredentials need an exact origin and Allow-Credentials
"CORS error" but the Network tab shows 500Fix the server error first
API URL hard-coded to localhostUse relative paths or a per-environment setting

CORS in Mythex

When a Mythex project has a website and an API server, both run as services in the same project, and you can switch between them in the preview. Asking the agent to have the frontend call the backend through a relative /api path, proxied in development, avoids most CORS problems. If you do need cross-origin calls, give the agent the exact error from the console and the origins involved. Check the result on the published app too, since its address differs from the preview. See Multi-service apps and API backends in the docs, and our guides on debugging an AI-built app and frontend vs backend.

CORS checklist

  • Read the full console error and the failing request's status code
  • Ruled out a hidden 404 or 500
  • Secret-key APIs called from the backend, not the browser
  • Server sends Access-Control-Allow-Origin with exact allowed origins
  • OPTIONS preflight answered with allowed methods and headers, before auth
  • CORS headers present on error responses
  • Credentials: exact origin, Allow-Credentials: true, correct cookie settings
  • Preview, published and custom-domain origins all covered, or relative /api paths used
  • No no-cors, public proxies, or reflect-any-origin shortcuts

Questions

What does a CORS error mean?

Your page, loaded from one origin, tried to read a response from a different origin, and that server's response didn't include headers allowing it. The browser blocks your code from reading the response. The fix is on the server, or in how the request is routed, not in the frontend code.

Can I fix a CORS error from the frontend?

Not properly. Settings like mode: 'no-cors' only hide the response from your code. You fix it by making the server send the right Access-Control-Allow-Origin header, or by calling the API from your own backend or a same-origin proxy.

Is Access-Control-Allow-Origin: * safe?

It's fine for public data that anyone may read, like a public API with no login. It can't be used with cookies or other credentials, and for private APIs you should list the exact origins that are allowed.

Why does my request work in Postman or curl but not in the browser?

CORS is enforced by browsers only. Tools like Postman and curl don't apply it, so a request can succeed there and still be blocked when page JavaScript makes it.

What is a preflight request?

For requests that aren't simple, such as a JSON POST or one with an Authorization header, the browser first sends an OPTIONS request asking the server which methods and headers are allowed. If that OPTIONS request fails or lacks the right headers, the real request is never sent.

Keep reading

  • How to Publish an App on Google Play: The Real Play Console Steps — How to publish an app on Google Play: developer account, Create app, store listing, App content forms, the 12-tester closed test and production release.
  • How to Publish an App on the App Store: TestFlight to Approval — How to publish an app on the App Store: Developer Program, App Store Connect, TestFlight, listing, App Privacy, submitting for review and rejections.
  • How to Use Google AI Studio: Playground, Build Mode and Vibe Coding With Gemini — How to use Google AI Studio: test Gemini in the Playground, get an API key, vibe code an app in Build mode, and know when to move to a full-stack builder.
  • Is Vibe Coding Safe? The Real Risks and What to Check Before You Publish — Is vibe coding safe? It can be. The real risks (data leaks, exposed keys, abuse), how to keep secrets safe, and a 30-minute check before you publish.
  • Vibe Coding for Beginners: How to Start, Step by Step — A vibe coding tutorial for complete beginners: pick a tool, write a first prompt, test, fix and publish a small app in one afternoon. No coding needed.
  • 30 Vibe Coding Prompts You Can Copy (Cheat Sheet) — 30 vibe coding prompts you can copy and paste, grouped by stage: first build, planning, design, data, fixing bugs, testing, security and publishing.

Start building free · Templates · Docs

አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)