Skip to content
Nilay Kabariya

Entry No.080·Fixes··3 min read

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

Blocked by CORS policy, but Postman and curl work fine. Five versions of the error reproduced in Chrome with Express and Vite, and which fix fits each.

by Nilay#node#viteFIXED

The error, verbatim

Jump to the fix ↓
Access to fetch at 'http://localhost:3001/plain/data' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Tested on

Browser
Chromium 152 (Chrome/152.0.7977.149)
API
Express 5.3.0, cors 2.8.6
Front end
Vite 8.3.4 dev server
Node / OS
24.14.0 / Windows 11 Pro
Contents
  1. Fix it on the API01
  2. The order of middleware matters02
  3. Sending cookies: no wildcard03
  4. Not your API: proxy it in development04
  5. What didn’t work05
  6. How this was tested06

Your page on one origin (here http://localhost:5173) called an API on another (http://localhost:3001). A different port is enough to count as a different origin. The browser sent the request, got a response, and then refused to let your JavaScript read it, because the response didn’t say this origin was allowed.

That’s why Postman and curl work: CORS is a browser rule, not a server one. I called the same URL with Node’s fetch and got {"ok":true}.

In your code, every version of this error looks identical: TypeError: Failed to fetch. The details are only in the browser console. I got five different versions in Chrome:

What the server did What the console said
Sent no CORS headers, plain GET No 'Access-Control-Allow-Origin' header is present on the requested resource.
Sent no CORS headers, POST with JSON Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present...
Set Access-Control-Allow-Origin: * by hand, POST with JSON Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
Allowed *, request sent with credentials: 'include' The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
Rejected the request (401) before the CORS code ran No 'Access-Control-Allow-Origin' header is present on the requested resource.

The second and third rows involve a preflight: before a POST with a JSON body (or any custom header), the browser first sends an OPTIONS request asking for permission. If that answer is wrong, the real request is never sent.

One look-alike that isn’t CORS: with the API not running at all, fetch also threw Failed to fetch, but the console said net::ERR_CONNECTION_REFUSED and nothing about CORS.

Fix it on the API

If the API is yours, it has to send the headers. In Express, add the cors package before your routes:

npm i cors
import cors from 'cors';

app.use(cors());

cors() with no options allows every origin. That’s fine for a public API. For anything else, name the site that may call it, as in the cookies section below.

The order of middleware matters

The last row of the table is the one that wastes the most time. My login check ran before cors() and answered 401. That response had no CORS headers, so the browser hid it, and the console reported a CORS problem instead of “not logged in”.

Put cors() first, before authentication, rate limits, or anything else that can answer early:

app.use(cors());
app.use(requireLogin);

An error thrown inside a route was fine either way: with cors() first, the 500 response kept its headers and was readable.

Sending cookies: no wildcard

With credentials: 'include' in fetch, the browser refuses Access-Control-Allow-Origin: *. The server has to name your origin and allow credentials.

app.use(cors({ origin: 'http://localhost:5173', credentials: true }));

Use your real front-end address in production.

Not your API: proxy it in development

If you can’t change the server, or just want local development to work, let the Vite dev server forward the requests. The browser then only talks to its own origin, so CORS never comes up.

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3001',
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
};

Then call fetch('/api/plain/data') instead of the full URL.

server.proxy is a setting of the dev server. Your production build needs its own way to reach the API: CORS headers on the API, or the same proxy setup on whatever serves the site.

What didn’t work

How this was tested

Windows 11 Pro, Node 24.14.0, October 19, 2026. An Express 5.3.0 API on port 3001 served the same routes with no CORS headers, with a hand-set *, with cors 2.8.6, with credentials allowed, and with a 401 check before and after cors(). A page on the Vite 8.3.4 dev server (port 5173) called each one with fetch in Chromium 152 (Claude’s built-in browser), then again through the Vite proxy. Every console message above is the real output.

— N.K., end of entry No.080

Useful? Pass it on:Post on XFollow @EmotionalMatter

Related entries

  1. No.077

    ReferenceError: process is not defined in Vite (dev vs build)

    In Vite 8, process.env.X crashes in dev but is silently undefined in the build; NODE_ENV works in both. Tested in Chromium: import.meta.env, define, polyfills.

    > Uncaught ReferenceError: process is not defined

    FIXED3 min
  2. No.018

    "Cannot find native binding" in Vite 8: the missing @rolldown file

    Vite 8 fails with "Cannot find native binding" and names @rolldown/binding-wasm32-wasi? That's a fallback, not your missing package. Both causes, and the fix.

    > Error: Cannot find native binding. npm has a bug related to optional dependencies

    FIXED8 minWindows
  3. No.093

    Fix: bigint Failed to load bindings, pure JS will be used

    bigint: Failed to load bindings, pure JS will be used. Why npm 12 shows it on every install, whether it matters, and how to get the native build back.

    > bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)

    FIXED3 minWindows

Post card · Every Sunday

What broke this week.

One email on Sundays: the Node, TypeScript, Python and AI SDK releases that broke builds that week, each one reproduced, with a link to the fix that worked. Never shared, and you can leave any time.

— Nilay

or follow by RSSor on X

By subscribing you agree to the privacy note. One click to leave.

tip: paste the exact error text