CORS is a browser mechanism, not a server bug. That's why Postman works but your deployed app doesn't. This is a step-by-step guide to fix it properly.
CORS is a browser mechanism. Whenever your app tries to send a request to a different host, the browser asks the server if it's allowed to do so. It's not a bug in your code — it's a security layer built into every browser. The problem is that most people were never taught how it works.
AI-generated code plus public deployment is where most people get burned. On localhost everything worked because the frontend and backend lived at the same address. After deployment — they're on different domains and the browser blocks the requests.
This article is a practical step-by-step guide. We explain what CORS actually is, why AI tools make it worse, and how to fix it once and for all — with code you can copy and use immediately.
CORS (Cross-Origin Resource Sharing) is a browser security mechanism, not a server error. Here's how it works: whenever your application tries to send an HTTP request to a different origin (a different domain, port, or protocol), the browser first asks the target server whether this is allowed.
On localhost, your frontend and backend usually run at the same address — for example http://localhost:3000. That's same-origin — same domain, same port, same protocol. The browser sees no issue because the request goes to the same host. This is why everything works during development.
After deployment, your frontend lives at https://myapp.com and your backend at https://api.myapp.com or an entirely different domain. That's cross-origin. And this is where the browser steps in.
Before the browser sends your actual request (POST, PUT, DELETE), it first sends a so-called preflight request — an OPTIONS request to the same URL. It's essentially asking: "Hey server, is the frontend from domain X allowed to send POST requests with these headers?" If the server doesn't respond with the right CORS headers, the browser blocks the request before it's even sent.
The key thing to understand: CORS is a browser mechanism. Postman, curl, server-to-server requests — none of them enforce CORS. That's why your API can work perfectly in Postman while throwing errors in the browser. It's not an API bug — it's the browser doing its job.
CORS is a browser mechanism. Whenever you try to send a request to another host, your browser asks if it's allowed to do so. That's why Postman works but your browser doesn't — Postman isn't a browser and doesn't enforce this rule.
Here's the typical scenario: you see a CORS error in the console. You tell AI "fix CORS." AI changes something in the server code. The CORS error disappears — but now something else is broken. You tell AI "fix that." AI rewrites another part of the code. And that's how the cascade begins.
I've seen this firsthand with clients: letting AI "fix CORS" rewrote server code, broke imports, database selects, and response shapes. Three days to undo what AI did in seconds.
The problem is fundamental: AI doesn't understand the boundary between frontend and backend. It doesn't know which code runs in the browser and which runs on the server. When you ask it to fix CORS, AI often modifies the frontend (adds mode: 'no-cors' to fetch) or changes server code in ways that break everything else.
The worst things AI does:
Access-Control-Allow-Origin: * everywhere — a wildcard instead of specific domains. Dangerous in production.OPTIONS requests — the preflight goes unanswered, so the browser blocks everything.mode: 'no-cors' to fetch on the frontend — this hides the problem, not fixes it. The response becomes "opaque" and you can't read any data.When things break, people react naturally: they keep prompting. "Fix this." "Now that doesn't work." "Revert the last change but keep the CORS fix." Each successive prompt makes things worse because AI doesn't remember why the code looked the way it did.
Letting AI "fix CORS" rewrote server code, broke imports, DB selects, and response shapes. Three days to undo what AI did in seconds. Don't keep prompting — stop and understand the problem.
Most people look at the CORS error in the browser console and assume it's a CORS problem. But often the real error is elsewhere. Open the Network tab in DevTools, not Console.
Find the failed request. Click on it and check the response code. If you see 500 Internal Server Error, your server crashed — and because it crashed, it didn't add CORS headers to the response. The browser sees the missing Access-Control-Allow-Origin header and shows a CORS error, but the real problem is a server error.
If the endpoint errors out (500), Access-Control-Allow-Origin usually won't be added. Fix the server error, and the "CORS error" will disappear on its own.
Here's what a CORS middleware should look like on an Express.js server. Notice that we set specific domains instead of wildcards, handle the OPTIONS request, and set credentials:
And here's what AI typically generates when you ask it to "fix CORS." It looks simple, but it's full of problems:
The differences are critical: the wildcard * means any website in the world can send requests to your API. No OPTIONS handling means preflight requests won't work. No Allow-Credentials means cookies and tokens won't be sent. This isn't "fixed" — it's an open security hole.
If you're using Supabase Edge Functions (Deno), CORS must be handled manually in each function — or, even better, centralized in a single helper file. Here's the correct pattern:
One file, one place. CORS configuration should live in one place across your entire project — not scattered across every endpoint and every function. Create a cors.js (or cors.ts) file and import it everywhere it's needed.
At the same time, lock down your JSON response shape — every endpoint should return data in the same format. Keep your entry file tiny — it should only import middleware and start the server. All business logic goes into separate files.
This way, when you need to change allowed origins or add a new header, you do it in one place, and the change works everywhere. Lack of centralization is one of the main reasons AI apps work locally but fail in production.
Sometimes everything looks correct and CORS still doesn't work. Here are the most common traps:
https://myapp.com is not the same as https://www.myapp.com. Watch out for trailing slashes — the browser sends the Origin header without a trailing slash, so if your allowlist includes https://myapp.com/, the string comparison will fail.no-cors mode in fetch: If someone (or AI) added mode: 'no-cors' to a fetch request, the browser won't throw an error — but the response will be "opaque." You can't read the status, headers, or body. This isn't a fix — it's sweeping the problem under the rug.http:// and https:// are different origins. If your frontend is on HTTPS and your backend on HTTP, CORS will block the request, and on top of that the browser may block mixed content.AI coding tools — Cursor, Claude Code, Copilot, and others — are incredibly effective at generating code. But CORS is one area where they consistently fail. Why? Because CORS is a configuration and architecture problem, not a code logic problem. AI excels at writing logic. It poorly understands deployment context.
Here are the recurring patterns I see with clients:
* in production — which is not only dangerous but makes it impossible to use credentials (cookies, tokens) in requests.OPTIONS requests — which means POST, PUT, and DELETE requests will be blocked by the browser, even if CORS headers are set correctly on regular responses.credentials: 'include' in fetch, the server must return Access-Control-Allow-Credentials: true and cannot use the wildcard * as origin. AI almost never understands this dependency.The best strategy: don't ask AI to fix CORS. Understand the problem yourself, write the configuration manually (or use the code from this article), and tell AI to stay away from configuration files.
Before you deploy your application, go through these points. Each one is a potential source of CORS errors in production. If one is unmet, your app may break in ways that are hard to diagnose.
* in production. List every domain your frontend can send requests from.Access-Control-Allow-Credentials: true and you cannot use a wildcard origin.On localhost, your frontend and backend usually run at the same address (e.g., http://localhost:3000), so the browser treats this as same-origin and doesn't apply CORS rules. After deployment, frontend and backend are on different domains — that's cross-origin and the browser starts checking for CORS headers. Additionally, many frameworks use a dev proxy that masks cross-origin — that proxy doesn't exist in production.
Only for fully public APIs that don't require authentication — e.g., public weather data or exchange rates. In every other case, use a list of specific domains. Remember: the wildcard * and Access-Control-Allow-Credentials: true cannot coexist. The browser will reject the response. If your application uses cookies, JWT tokens, or any form of session, the wildcard is not an option.
CORS is a backend configuration triggered by the frontend. The error appears in the browser (frontend), but the fix is on the server (backend). The server must return proper Access-Control-Allow-* headers for the browser to allow the request. You cannot "fix CORS" with frontend code changes alone — the only thing you can do on the client side is hide the problem (e.g., mode: 'no-cors'), which is worse.
Postman is not a browser. Browsers enforce CORS — API clients don't. Postman, curl, Insomnia, server-to-server requests — none of them send preflight requests or check Access-Control-Allow-Origin headers. It's a pure browser mechanism. If your API works in Postman but not in the browser, it's 100% a CORS configuration issue on the server — not the API itself.
This is the CORS cascade. AI doesn't understand your project's context. When you ask it to fix CORS, it often modifies files that shouldn't be touched — changes imports, modifies response shapes, rewrites middleware. Each successive fix makes things worse. The solution: roll back to the state before AI's "fix" (use git), understand the CORS problem using this article, and write the configuration manually. Don't let AI edit CORS configuration files.
CORS errors are often just the tip of the iceberg. If your AI app has issues after deployment, let's talk — we'll help you get from prototype to production.
Book a free call →