← All articles

cors localhost

CORS Localhost Errors: How to Fix Local Testing Issues

CORS errors on localhost are browser security signals, not random dev server bugs. Fix the server headers or test through the right origin.

Published May 14, 2026 4 min read
In this article

CORS localhost errors happen when a browser page loaded from one origin asks another origin for data, and the server response does not grant browser access.

The request may reach the server. The server may even return a valid response. The browser still blocks JavaScript from reading it if the CORS headers do not match the requesting origin.

CORS localhost: what is failing?

CORS stands for Cross-Origin Resource Sharing. MDN defines it as an HTTP-header mechanism that lets a server indicate which origins, other than its own, a browser may permit when loading resources.

An origin is the combination of scheme, host, and port. These are different origins:

http://localhost:3000
http://localhost:5173
https://localhost:3000
https://example.ngrok-free.app

During local development, your frontend might run on http://localhost:3000 while your API runs on http://localhost:8080. Same machine, different port. To the browser, that is cross-origin.

Fix the server, not the browser

The right fix usually lives on the API server. The server must return CORS headers that match the frontend origin.

For a simple local Express API, you might configure one allowed origin:

import cors from "cors";
import express from "express";

const app = express();

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

For multiple local ports, use an allowlist:

const allowedOrigins = new Set([
  "http://localhost:3000",
  "http://localhost:5173",
]);

app.use(cors({
  origin(origin, callback) {
    if (!origin || allowedOrigins.has(origin)) {
      callback(null, true);
      return;
    }

    callback(new Error("Origin not allowed"));
  },
  credentials: true,
}));

Avoid disabling browser security flags for normal work. That hides the problem on your machine and leaves your teammates, reviewers, and production users with the same failure.

Understand preflight requests

Some requests trigger a CORS preflight. The browser sends an OPTIONS request before the real request. That preflight asks whether the actual method and headers are allowed.

MDN describes preflight behavior for requests with methods or headers outside the simple request set. Common local triggers include:

  • Authorization headers.
  • Content-Type: application/json on some requests.
  • Custom headers such as X-Client-Version.
  • Methods like PUT, PATCH, or DELETE.

The API must answer the preflight with headers such as:

Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

If the preflight fails, the browser blocks the real request. Your server logs may only show an OPTIONS request, which can make the actual API route look unused.

Credentials need exact origins

Cookies and authentication add one common trap. If a request includes credentials, the server cannot use Access-Control-Allow-Origin: *. It must return a specific origin and include Access-Control-Allow-Credentials: true.

That means this is wrong for cookie-based local auth:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Use the exact frontend origin instead:

Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: true
Vary: Origin

Also check cookie attributes. SameSite, Secure, and domain settings can block cookies even after CORS headers look correct.

When a tunnel helps with CORS localhost issues

A tunnel does not fix a misconfigured API. The API still needs correct CORS behavior.

A tunnel helps when the problem comes from local-only origins, HTTPS requirements, or reviewer access. A public HTTPS review URL can make your local app behave closer to the deployed origin. It also lets phones, clients, and external services reach a server that would otherwise live only on your machine.

When you test through a tunnel, add the tunnel origin to your API allowlist for that session. That keeps the fix honest: the browser sees a real origin, and the server grants that origin on purpose.

With wiremaven:

npx wiremaven-cli 3000 --expires 30m

wiremaven creates a temporary encrypted public link for your local app. The local machine connects to the relay through an outbound-only WebSocket. Reviewers open a browser URL, and you see live viewer, request, and failure signals while they test.

Use the docs for setup and how wiremaven works for the request path.

Local CORS debugging checklist

Work through the browser console and Network panel:

  • Confirm the frontend origin: scheme, host, and port.
  • Confirm the API response includes Access-Control-Allow-Origin.
  • Confirm credentialed requests do not use *.
  • Check whether an OPTIONS preflight fails before the real request.
  • Add Vary: Origin when responses vary by origin.
  • Make sure redirects do not change the preflight target.
  • Test the same flow through the URL your reviewer will use.

For local mobile checks, read mobile browser testing. For tunnel basics, read what is a localhost tunnel.

FAQ

Why does CORS fail on localhost?

Different localhost ports are different origins. If your frontend and API use different ports, the API must return CORS headers that allow the frontend origin.

Can I use Access-Control-Allow-Origin star locally?

Only for non-credentialed requests. If cookies or credentials are involved, return the exact origin instead of *.

Does a tunnel fix CORS?

It can help by giving your app a stable HTTPS origin for review, but it does not replace correct API headers.

Should I disable CORS in my browser?

No. Fix the server response. Browser flags create false local success and hide problems from the actual review path.


Related: Mobile Browser Testing During Local Development - What Is a Localhost Tunnel?