How To Guide

How to Fix CORS Errors — Every Common Cause + Fix (2026)

The real reasons "Access-Control-Allow-Origin" errors happen and how to fix each one — preflight requests, credentials, wildcard origins, cache issues, and dev vs prod.

What CORS actually is

CORS (Cross-Origin Resource Sharing) is a browser security feature. Servers advertise WHICH origins are allowed to read their responses via the Access-Control-Allow-Origin header. Browsers refuse to expose the response to JavaScript when the origin doesn't match. The server always ANSWERS the request — CORS just controls whether the browser reveals that answer to your code.

Key implication: CORS errors are NEVER security holes. They are the browser telling you: "the server said something but didn't authorize you to read it."

The most common CORS errors — and their fix

1. "No 'Access-Control-Allow-Origin' header is present on the requested resource"

Cause: Server didn't send the header at all.
Fix: Add on the server:

Access-Control-Allow-Origin: https://your-app.com

Or for public APIs:

Access-Control-Allow-Origin: *

Wildcards do NOT work if you also need credentials. See error 3 below.

2. "The 'Access-Control-Allow-Origin' header has a value 'https://foo.com' that is not equal to the supplied origin"

Cause: The server hardcoded ONE origin but you're requesting from a different one.
Fix: Read the browser's Origin header on the server side, check it against your allowlist, and echo it back:

// Express example
const allowed = ['https://app.com', 'https://staging.app.com', 'http://localhost:3000'];
if (allowed.includes(req.headers.origin)) {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
  res.setHeader('Vary', 'Origin'); // critical for caching correctness
}

The Vary: Origin header tells caches (Cloudflare, browser cache) that the response depends on the origin — otherwise a request from origin A can get cached and served for origin B.

3. "The value of the 'Access-Control-Allow-Credentials' header is 'true' but the 'Access-Control-Allow-Origin' is '*'"

Cause: You want cookies/auth to be sent, but you're using wildcard origin. Browsers REFUSE this combination.
Fix: Echo the specific origin (as in error 2), not the wildcard. Also set Access-Control-Allow-Credentials: true on both the preflight OPTIONS and the actual response. And on the client:

fetch(url, { credentials: 'include' })

4. "Response to preflight request doesn't pass access control check"

Cause: The browser sent a preflight OPTIONS request but your server didn't respond correctly (returned 404, 405, or didn't send the CORS headers on the preflight).
Fix: Handle OPTIONS explicitly:

// Express: put BEFORE your routes
app.options('*', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Access-Control-Max-Age', '86400');
  res.sendStatus(204);
});

5. "Method DELETE is not allowed by Access-Control-Allow-Methods"

Cause: Your preflight response listed some methods but not the one you're using.
Fix: Include all methods you use in the header. GET and POST for simple requests don't need to be listed (they're always allowed) but everything else does.

6. "Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers"

Cause: You're sending a custom header the server didn't authorize.
Fix: Add it to Access-Control-Allow-Headers on the preflight response.

Which requests trigger a preflight?

The browser sends a preflight OPTIONS FIRST if the request is not a "simple request." A simple request is:

  • Method: GET, HEAD, or POST
  • Only "safelisted" headers: Accept, Accept-Language, Content-Language, Content-Type (limited to a few values)
  • Content-Type is application/x-www-form-urlencoded, multipart/form-data, or text/plain

Everything else preflights. This means any JSON POST (Content-Type: application/json) preflights. Any request with an Authorization header preflights. Any DELETE, PUT, or PATCH preflights.

Development workarounds (NOT production)

Vite / Webpack dev proxy

Proxy your API calls through the dev server — same origin, no CORS at all:

// vite.config.js
export default {
  server: {
    proxy: {
      '/api': 'http://localhost:4000'
    }
  }
}

Browser flag (do NOT use for production dev)

Chrome can be launched with --disable-web-security --user-data-dir=/tmp/chrome-nocors. Useful for one-off debugging. Do NOT use this browser instance for real browsing.

CORS proxy (for reading public APIs from a static site)

Route the request through a small proxy you control that adds the CORS headers. Cloudflare Workers is a great host for this — 30 lines of code, free tier covers most static-site use cases. See how we do it on the newspaper directory: a Cloudflare Worker fetches the publisher's RSS and adds Access-Control-Allow-Origin: *.

Production don'ts

  • Don't use Access-Control-Allow-Origin: * on an authenticated API — it means anyone's site can read your API's responses when a user is logged in.
  • Don't blindly echo the Origin without an allowlist — that's the same as * with extra steps.
  • Don't forget Vary: Origin — caches will serve the wrong CORS headers otherwise.
  • Don't skip the OPTIONS handler — a lot of CORS "bugs" are just missing preflight handling.

Related

What is CORS? (deeper explainer) · HTTP 403 Forbidden · HTTP 401 Unauthorized.

Featured Tools

Try these free tools directly in your browser — no sign-up required.

fix cors error access-control-allow-origin error cors preflight failing cors credentials error no access-control-allow-origin cors error localhost

Explore 300+ Free Tools

Utilko has tools for developers, writers, designers, students, and everyday users — all free, all browser-based.