VibeSecurity

Fix

Fix: No 'Access-Control-Allow-Origin' header is present (CORS)

This error comes from the browser, not from your API. Your page on one origin asked for data from another origin, and the response did not say that your page is allowed to read it.

By the VibeSecurity team3 min read

What this means

Chrome reports: Access to fetch at '...' from origin '...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Firefox words it as: Reason: CORS header 'Access-Control-Allow-Origin' missing. Browsers stop scripts from reading responses from another origin (a different scheme, domain or port) unless the server opts in with that header. The request usually reached the server. The browser is refusing to hand the response to your code.

Why it happens

  • The API never sets CORS headers. Generated backends often work when tested with curl or on the same origin and fail from the deployed frontend.
  • The allow-list names the wrong origin: http instead of https, a missing www, a trailing slash, or localhost only.
  • The preflight OPTIONS request is not handled. Requests with JSON bodies or an Authorization header trigger a preflight, and a 404 or 405 on OPTIONS produces this error.
  • The server crashed or returned an error page from a proxy or host, and that error response carries no CORS headers. The real bug is the 500, not CORS.
  • You are calling a third-party API that does not allow browser calls at all, often because it expects a secret key that should not be in a browser.

How to fix it

  1. 1Open the Network tab, click the failed request and read the status code. If it is a 4xx or 5xx, fix that error first.
  2. 2On the server that serves the API, return Access-Control-Allow-Origin with the exact origin of your site. Keep an allow-list for production, preview and local development.
  3. 3When you echo one origin from a list, also send Vary: Origin so caches do not mix responses.
  4. 4Handle OPTIONS requests and return Access-Control-Allow-Methods and Access-Control-Allow-Headers for what your frontend sends.
  5. 5If you send cookies, set Access-Control-Allow-Credentials: true and never combine it with a wildcard origin. Browsers reject that combination.
  6. 6If you do not control the API, call it from your own server route and have the browser call your route instead. This also keeps the third party's key out of the browser.
Next.js route handler with an origin allow-list
const allowedOrigins = new Set([
  'https://www.yourapp.com',
  'https://yourapp.com',
  'http://localhost:3000',
]);

function corsHeaders(origin: string | null): HeadersInit {
  if (!origin || !allowedOrigins.has(origin)) return { Vary: 'Origin' };
  return {
    'Access-Control-Allow-Origin': origin,
    'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    Vary: 'Origin',
  };
}

export async function OPTIONS(request: Request) {
  return new Response(null, {
    status: 204,
    headers: corsHeaders(request.headers.get('origin')),
  });
}

export async function GET(request: Request) {
  return Response.json(
    { ok: true },
    { headers: corsHeaders(request.headers.get('origin')) },
  );
}

How to confirm the fix

Send your own API a request with an Origin header and read the response headers. Your real origin should be echoed back. An origin you do not own should get no Access-Control-Allow-Origin header.

curl: check the header and the preflight on your own API
curl -s -D - -o /dev/null https://api.yourapp.com/items \
  -H 'Origin: https://www.yourapp.com'

curl -s -D - -o /dev/null -X OPTIONS https://api.yourapp.com/items \
  -H 'Origin: https://www.yourapp.com' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type, authorization'

curl -s -D - -o /dev/null https://api.yourapp.com/items \
  -H 'Origin: https://not-your-site.example'

Frequently asked questions

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

CORS is enforced by browsers only. Other clients ignore it. That is also why CORS is not a security control for your API: it protects users' browsers, not your server.

Can I fix this in my frontend code?

No. The header must come from the server that owns the resource. The frontend options are to call your own backend instead, or to use a development proxy locally.

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

For public data that needs no login, yes. For anything tied to a user session, use an explicit allow-list.

I get this error from a Supabase Edge Function. What is different?

Edge Functions do not add CORS headers for you. Handle the OPTIONS request and add the headers to every response, including error responses, as the Supabase CORS guide shows.

Sources

  1. 1.MDN: Cross-Origin Resource Sharing (CORS)
  2. 2.MDN: Reason: CORS header 'Access-Control-Allow-Origin' missing
  3. 3.MDN: Access-Control-Allow-Origin
  4. 4.WHATWG Fetch Standard: CORS protocol
  5. 5.Supabase: CORS for Edge Functions