CORS Explained in 2026: Fixing Cross‑Origin Errors Fast

March 13, 2026 · Security & Auth, Web APIs, DevOps

CORS (Cross-Origin Resource Sharing) is a browser security policy that blocks a web page from making requests to a different origin unless the server explicitly allows it. If you’ve seen errors like “No ‘Access-Control-Allow-Origin’ header is present” in your console, you’ve hit CORS.

This guide explains exactly why CORS happens, how preflight requests work, and how to fix cross-origin errors in production and local development. It’s designed to be practical and bookmark‑worthy, with concrete fixes for common stacks.

What “origin” actually means

An origin is the combination of scheme, host, and port. These three values must all match for two URLs to be same‑origin.

CORS only applies to requests made by browsers (like fetch, XHR, or browser‑based GraphQL clients). Server‑to‑server requests are not blocked by CORS.

Why browsers enforce CORS

Browsers enforce CORS to prevent malicious sites from reading data from other origins using a user’s existing authenticated session. CORS is not about blocking the request itself; it’s about controlling whether the response is readable by the browser.

That’s why you can often see the network request go through, yet JavaScript still fails to read the response with a CORS error.

Typical CORS error messages (and what they mean)

Simple requests vs. preflighted requests

CORS behaves differently depending on request type:

Simple requests (no preflight)

These are allowed if the response includes the right CORS headers. A request is “simple” if it uses:

Preflighted requests (OPTIONS)

If you use JSON, custom headers, or methods like PUT/DELETE, the browser sends an OPTIONS preflight to ask the server for permission first.

CORS response headers you must understand

Fixing CORS on the server (real examples)

The correct fix is almost always on the server. Here are practical examples.

Node.js (Express)

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

const app = express();

app.use(cors({
  origin: ["https://app.example.com", "https://admin.example.com"],
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true,
  maxAge: 600
}));

app.get("/api/profile", (req, res) => {
  res.json({ id: 1, name: "Ada" });
});

app.listen(8080);

Tip: If you allow credentials, you cannot use * for Access-Control-Allow-Origin. Use a specific origin list.

Python (FastAPI)

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.example.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
    max_age=600
)

@app.get("/api/profile")
def profile():
    return {"id": 1, "name": "Ada"}

Go (net/http)

func cors(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    origin := r.Header.Get("Origin")
    if origin == "https://app.example.com" {
      w.Header().Set("Access-Control-Allow-Origin", origin)
      w.Header().Set("Access-Control-Allow-Credentials", "true")
      w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
      w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
      w.Header().Set("Vary", "Origin")
    }

    if r.Method == http.MethodOptions {
      w.WriteHeader(http.StatusNoContent)
      return
    }

    next.ServeHTTP(w, r)
  })
}

Common mistakes that cause CORS failures

Debug CORS quickly (repeatable checklist)

When testing payloads, the JSON Formatter makes it easy to validate responses and ensure your API is returning valid JSON before you chase CORS ghosts.

Local development fixes

Local development is where CORS pain peaks because your frontend and backend run on different ports.

Option A: Use a local proxy (best dev experience)

Webpack, Vite, Next.js, and CRA all support a dev proxy. This keeps the browser on one origin.

// vite.config.js
export default {
  server: {
    proxy: {
      "/api": "http://localhost:8080"
    }
  }
}

Option B: Enable CORS in dev only

Allow localhost origins like:

allow_origins=["http://localhost:3000", "http://127.0.0.1:5173"]

Option C: Use a reverse proxy (Nginx)

server {
  listen 80;
  server_name app.local;

  location /api/ {
    proxy_pass http://localhost:8080/;
  }
}

Handling JSON and custom headers safely

When you send JSON or auth tokens, you trigger preflight. That’s normal—just allow it explicitly.

fetch("https://api.example.com/user", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + token
  },
  credentials: "include",
  body: JSON.stringify({ plan: "pro" })
});

If this fails, check Access-Control-Allow-Headers includes Authorization and Content-Type, and that the server responds to OPTIONS with a 204 or 200.

CORS vs. CSRF vs. auth headers

CORS doesn’t replace authentication. It’s a browser read‑protection mechanism. You still need proper auth (JWT, session cookies, OAuth) and CSRF protections when using cookies.

A helpful mental model:

Hardening CORS for production

Practical debugging tools

Here are a few DevToolKit helpers that make CORS troubleshooting faster:

Example: Full CORS setup with credentials

This is a common production pattern for a React frontend and API on separate subdomains.

// Express + CORS with credentials
app.use(cors({
  origin: "https://app.example.com",
  credentials: true,
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  maxAge: 600
}));

Browser request:

fetch("https://api.example.com/me", {
  credentials: "include"
});

Required response headers:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: Origin

Key takeaways

FAQ

Need to validate or debug your API output quickly? Use the JSON Formatter to verify response structure before chasing CORS errors.

Recommended Tools & Resources

Level up your workflow with these developer tools:

Auth0 → Cloudflare Zero Trust → Web Application Security by Andrew Hoffman →

Dev Tools Digest

Get weekly developer tools, tips, and tutorials. Join our developer newsletter.