CV
July 05, 2026

Fix CORS Errors That Work on Localhost But Break in Production (MERN)

Learn why CORS errors appear only after deploying a MERN app and how to fix them โ€” covering origin config, credentials, cookie flags, and preflight requests.

Fix CORS Errors That Work on Localhost But Break in Production (MERN)
๐Ÿ“… July 5, 2026 ๐Ÿท๏ธ Deployment Troubleshooting โฑ๏ธ 7 min read Express ยท MERN

Fix CORS Errors That Work on Localhost But Break in Production (MERN)

A practical guide to why CORS breaks only after deployment, with exact Express.js fixes for credentials, cookies, and cross-domain preflight requests.

If your MERN app works perfectly on localhost but throws Access to fetch has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present the moment you deploy, your backend's CORS config is hardcoded for a dev environment that no longer exists in production.

โŒ Common misconception: CORS is not "broken" in production โ€” it's working exactly as designed. Your frontend and backend are now on genuinely different origins (different domains, subdomains, or ports), so the browser enforces the rules it always silently skipped in dev.

Why It Works Locally But Not in Production

Locally, many devs run frontend and backend close enough (same port, proxy config, or browser extensions disabled) that CORS restrictions barely surface. In production, your frontend is usually on https://myapp.com and your API is on https://api.myapp.com or a separate host like Render/Railway entirely โ€” a genuine cross-origin request, so the browser enforces the full CORS handshake.

Root Causes, Ranked by Frequency

  • ๐ŸŒ origin in your CORS config is still set to http://localhost:3000
  • ๐Ÿช Using credentials: true on the frontend but Access-Control-Allow-Origin: * on the backend (invalid combination)
  • ๐Ÿ”’ Cookies not sent because SameSite/Secure flags aren't set for a cross-domain setup
  • ๐Ÿšง Preflight OPTIONS requests never reaching your CORS middleware (blocked by a proxy, load balancer, or route order)

Fix 1: Set the Correct Origin Per Environment

Never hardcode a single origin. Use an environment variable so dev and prod each get the correct value, and allow multiple origins if you have staging/preview deployments.

import cors from "cors";

const allowedOrigins = [
  "http://localhost:3000",
  "https://myapp.com",
  "https://staging.myapp.com",
];

app.use(
  cors({
    origin: (origin, callback) => {
      if (!origin || allowedOrigins.includes(origin)) {
        callback(null, true);
      } else {
        callback(new Error("Not allowed by CORS"));
      }
    },
    credentials: true,
  })
);

Fix 2: Never Combine Wildcard Origin With Credentials

The browser will reject the response outright if Access-Control-Allow-Origin: * is paired with a credentialed request. If you need cookies or auth headers sent cross-origin, you must echo back a specific origin.

// โŒ Invalid with credentials
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

// โœ… Valid: specific origin + credentials
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Credentials: true

// Frontend fetch must also opt in:
fetch("https://api.myapp.com/data", {
  credentials: "include",
});

Fix 3: Cookie SameSite and Secure Flags

If your frontend and API are on different subdomains or domains, cookies set with the default SameSite=Lax will silently fail to be sent on cross-site requests.

res.cookie("token", jwtToken, {
  httpOnly: true,
  secure: true,        // required in production (HTTPS)
  sameSite: "none",    // required for cross-domain requests
});

Fix 4: Make Sure CORS Middleware Runs Before Your Routes

If app.use(cors()) is placed after your route handlers, or after another middleware that terminates the request, the preflight OPTIONS request never gets the right headers.

const app = express();

// โœ… CORS must be one of the very first middleware
app.use(cors({ origin: allowedOrigins, credentials: true }));
app.use(express.json());

// routes go after
app.use("/api/users", userRoutes);
โœ… Best Practice: Keep allowed origins in an environment variable (comma-separated list), never in hardcoded strings, so staging and preview deployments don't need a code change to work.

Diagnostic Checklist

  • โœ… Open DevTools โ†’ Network tab โ†’ check if the OPTIONS preflight request even reaches your server
  • ๐Ÿ” Confirm the response has Access-Control-Allow-Origin matching your exact frontend URL
  • ๐Ÿช If using cookies, confirm credentials: "include" is set on every fetch/axios call
  • ๐Ÿ›ก๏ธ Check your hosting platform (Vercel/Render/Nginx) isn't stripping CORS headers via its own proxy layer
  • ๐Ÿ“‹ Verify NODE_ENV is actually set to production and loading the right env vars

Official-Style Error Explanation

No 'Access-Control-Allow-Origin' header is present on the requested resource indicates the server's response did not include the required CORS header for the requesting origin. This is a server-side configuration issue, not a frontend bug. Configure the backend to explicitly allow the production origin and retry the request.

Frequently Asked Questions

โ“ Can I fix CORS from the frontend only?

No. CORS headers must come from the server responding to the request. Frontend code can request credentials be included, but it cannot grant itself permission the backend hasn't allowed.

โ“ Is using a proxy instead of fixing CORS a good idea?

It works as a workaround (e.g. Next.js API routes or an Nginx reverse proxy making requests same-origin), but it's usually better to fix CORS properly unless you have another architectural reason for a proxy layer.

โ“ Why does Postman work fine but the browser fails?

Postman doesn't enforce CORS โ€” it's a browser security feature only. A successful Postman request only confirms your API logic works, not that your CORS headers are correct.


CORS errors in production almost always trace back to an origin, credentials, or cookie-flag mismatch between environments โ€” fix the config once per environment and it stays fixed.

cors error mern
cors works on localhost not production
access-control-allow-origin missing
express cors credentials fix
cors preflight error node.js
cors error react express deployment
samesite cookie cross domain error
fix cors production vercel render
mern stack cors troubleshooting
no access-control-allow-origin header present
Share: