πŸ“ Tutorials

What is CORS? A Simple Explanation for Developers


CORS (Cross-Origin Resource Sharing) is a browser security feature that blocks web pages from making requests to a different domain than the one that served the page.

If your frontend is on localhost:3000 and your API is on localhost:8080, the browser considers that a cross-origin request and blocks it β€” unless the API explicitly allows it.

Why CORS exists

Without CORS, any website could make requests to any other website using your cookies. Imagine visiting evil-site.com and it silently makes requests to your-bank.com using your logged-in session. CORS prevents this.

The rule: a web page on domain-a.com can only make requests to domain-a.com. Requests to domain-b.com are blocked unless domain-b.com says β€œI allow requests from domain-a.com.”

What a CORS error looks like

Access to fetch at 'http://localhost:8080/api/users' from origin
'http://localhost:3000' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested resource.

The browser made the request, the server responded, but the browser refused to show you the response because the server didn’t include the right CORS headers.

How to fix it

The fix is always on the server side. The server needs to include headers that tell the browser β€œthis origin is allowed.”

Node.js (Express):

const cors = require('cors');
app.use(cors());  // Allow all origins

// Or be specific
app.use(cors({
  origin: 'http://localhost:3000',
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
}));

Python (FastAPI):

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_methods=["*"],
    allow_headers=["*"],
)

Nginx:

location /api/ {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
    add_header Access-Control-Allow-Headers "Authorization, Content-Type";
}

For the full fix guide with more examples, see CORS error fix.

How CORS actually works

Simple requests (GET, POST with simple headers): the browser sends the request and checks the response headers.

Preflight requests (PUT, DELETE, custom headers): the browser sends an OPTIONS request first to ask β€œam I allowed?” If the server says yes, the browser sends the real request.

Browser β†’ OPTIONS /api/users (preflight)
Server β†’ 200 OK + Access-Control-Allow-Origin: http://localhost:3000

Browser β†’ PUT /api/users/42 (actual request)
Server β†’ 200 OK + data

Common CORS scenarios

ScenarioCORS needed?
Frontend and API on same domainNo
Frontend on localhost:3000, API on localhost:8080Yes
Frontend on app.com, API on api.app.comYes (different subdomain)
Server-to-server requestsNo (CORS is browser-only)
Using a proxy in developmentNo (proxy makes it same-origin)

The development proxy trick

Instead of configuring CORS, proxy API requests through your dev server:

// vite.config.ts
export default {
  server: {
    proxy: {
      '/api': 'http://localhost:8080',
    },
  },
};

Now /api/users goes through localhost:3000 (your Vite server) which forwards it to localhost:8080. The browser sees same-origin, no CORS needed.

This only works in development. In production, configure CORS properly on the server.