afterbuild/ops
ERR-192/Browser · CORS
ERR-192
Access to fetch at 'https://api.example.com' from origin 'https://myapp.com' has been blocked by CORS policy

appears when:After deploying to a real domain when the fetch worked locally on http://localhost:3000

CORS error in production only

The server is responding — the browser refuses to hand the data to your JavaScript because Access-Control-Allow-Origin does not match the live domain.

Last updated 17 April 2026 · 5 min read · By Hyder Shah
Direct answer
CORS error in production is almost always an allow-list that lists only http://localhost:3000. For Supabase/Clerk, add the production origin in the dashboard. For a first-party Next.js API route, return Access-Control-Allow-Origin matching the request origin plus a handler for OPTIONS preflights. Never use * with credentials: "include".

Quick fix for CORS error in production

middleware.ts
typescript
01// middleware.ts — add CORS headers for /api/* routes02import { NextResponse } from "next/server";03import type { NextRequest } from "next/server";04 05const ALLOWED = ["https://myapp.com", "https://www.myapp.com"];06 07export function middleware(req: NextRequest) {08  const origin = req.headers.get("origin") ?? "";09  const allowed = ALLOWED.includes(origin) ? origin : ALLOWED[0];10 11  if (req.method === "OPTIONS") {12    return new NextResponse(null, {13      status: 204,14      headers: {15        "access-control-allow-origin": allowed,16        "access-control-allow-methods": "GET, POST, OPTIONS",17        "access-control-allow-headers": "content-type, authorization",18        "access-control-allow-credentials": "true",19        "access-control-max-age": "86400",20        "vary": "origin",21      },22    });23  }24 25  const res = NextResponse.next();26  res.headers.set("access-control-allow-origin", allowed);27  res.headers.set("access-control-allow-credentials", "true");28  res.headers.set("vary", "origin");29  return res;30}31 32export const config = { matcher: "/api/:path*" };
Paste at the repo root — handles preflight OPTIONS and mirrors the origin for credentials

Deeper fixes when the quick fix fails

01 · Proxy client fetches through a Next.js API route

CORS only fires for browser requests. Move the fetch to a server route. The browser hits your own origin, then your server fetches Supabase/Stripe with no CORS check. Also hides the API key.

app/api/supabase/search/route.ts
typescript
01// app/api/supabase/search/route.ts02export async function POST(req: Request) {03  const { query } = await req.json();04  const r = await fetch(`${process.env.SUPABASE_URL}/rest/v1/tasks?name=ilike.${query}`, {05    headers: {06      apikey: process.env.SUPABASE_SERVICE_ROLE_KEY!,07      authorization: `Bearer ${process.env.SUPABASE_SERVICE_ROLE_KEY}`,08    },09  });10  return Response.json(await r.json());11}

02 · Return the exact origin when using credentials

Browsers reject Access-Control-Allow-Origin: * paired with credentials: "include". Echo the request origin (from an allow-list) and add Vary: Originso CDNs do not cache one origin’s response for another.

Why AI-built apps hit CORS error in production

Every third-party API keeps a list of origins that are allowed to call it from a browser. Supabase calls it “Allowed Origins.” Clerk uses instance settings. AI builders add http://localhost:3000 during preview. The production domain is never added because the builder does not know what you will deploy to. The first real user hits the live page, the app tries to fetch Supabase, and the browser blocks.

The second cause is first-party API routes that return data to a client on a different subdomain. A Next.js app at app.myapp.com and an API at api.myapp.com are different origins. The API route must return Access-Control-Allow-Origin: https://app.myapp.com. AI-generated API routes rarely set these headers because they are unnecessary in the monolithic preview.

The third pattern is the preflight failure. Browsers send an OPTIONS request before any fetch with custom headers or credentials: "include". The AI-generated handler responds to GET/POST but not OPTIONS. The preflight 404s. The browser surfaces a generic CORS error. Fix by handling OPTIONS or using Next.js middleware.

CORS error in production by AI builder

How often each AI builder ships this error and the pattern that produces it.

AI builder × CORS error in production
BuilderFrequencyPattern
LovableEvery Supabase scaffoldAdds localhost only — prod origin missing
Bolt.newCommonNo OPTIONS handler on API routes
v0CommonUses wildcard * with credentials include
CursorSometimesSplits app and API into different subdomains without CORS config
Replit AgentRareSkips Vary: Origin — CDN serves wrong origin header

Related errors we fix

Stop CORS error in production recurring in AI-built apps

Still stuck with CORS error in production?

Emergency triage · $299 · 48h turnaround
We restore service and write the root-cause report.
start the triage →

CORS error in production questions

What does 'Access-Control-Allow-Origin' missing actually mean?+
CORS (Cross-Origin Resource Sharing) is a browser security feature. When a page at https://myapp.com fetches https://api.example.com, the browser sends an Origin header and expects Access-Control-Allow-Origin matching that origin. If missing or mismatched, the browser blocks the response. The request reached the server — the browser just refuses to hand the data to your JavaScript.
Why does CORS fail only in production?+
Because localhost and the production domain are different origins. Supabase, Stripe, and Clerk maintain an allow-list per project. The AI builder added localhost during preview. The production domain was never added. Your fetch from https://myapp.com hits Supabase with Origin: https://myapp.com and the response comes back allowing http://localhost:3000. Browser blocks. Add the production domain to the allow-list.
Can I fix CORS with a server proxy?+
Yes, and it is often the cleanest fix. Move the call from client to server. A Next.js API route at /api/supabase/search fetches Supabase server-side and returns JSON to your client. The browser never hits Supabase directly, so no CORS check fires. This hides the API key from the client and makes rate limiting easier.
What is a preflight request?+
For any request that is not a simple GET or POST with basic headers, the browser first sends an OPTIONS request to the target. The server must respond with Access-Control-Allow-Methods, Access-Control-Allow-Headers, and Access-Control-Allow-Origin. If the preflight fails, the real request never fires. A common AI-built bug is an API route that handles GET/POST but not OPTIONS — preflights 404 and the browser shows a generic CORS error.
How long does a CORS fix take?+
Ten minutes if you can identify the origin that is blocked and have admin access to the target service's allow-list. Most Supabase and Clerk fixes are a single Save button click. For a Next.js middleware CORS fix, it is two files of code and a redeploy. Emergency Triage at $299 covers intermittent CORS fails — that pattern usually means a cookie or credentials flag is misconfigured.
Next step

Ship the fix. Keep the fix.

Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.

About the author

Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.

Sources