Frontend Engineering/
Lesson

You asked AI to scaffold a backend, pointed your frontend at it, and instantly hit a wall of red CORSWhat is cors?Cross-Origin Resource Sharing - a browser security rule that blocks web pages from making requests to a different domain unless that domain explicitly allows it. errors. So you did what everyone does, you asked AI to fix it. The AI slapped Access-Control-Allow-Origin: * on every response and the errors vanished. Shipped it. Three weeks later, your authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. cookies stopped working in production. Understanding what CORS actually does, and why AI's default fix is almost always the wrong one, turns a recurring headache into a five-minute configuration.

What an originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443. is

Browsers define an "origin" as the combination of three things: the protocolWhat is protocol?An agreed-upon set of rules for how two systems communicate, defining the format of messages and the expected sequence of exchanges. (http vs https), the hostname, and the port. All three must match for two URLs to share the same origin.

https://example.com:443/page-a
https://example.com:443/page-b   <- same origin (path doesn't matter)

http://localhost:3000             <- different protocol AND port from the next one
https://localhost:3000            <- different protocol

https://example.com
https://api.example.com           <- different subdomain = different origin

https://example.com
https://example.com:8080          <- different port = different origin

The same-origin policyWhat is same-origin policy?A browser security rule that prevents JavaScript on one origin from reading responses from a different origin. is baked into every browser. When your JavaScript at http://localhost:3000 tries to fetch from http://localhost:4000, the browser sees two different origins and steps in.

02

Why CORSWhat is cors?Cross-Origin Resource Sharing - a browser security rule that blocks web pages from making requests to a different domain unless that domain explicitly allows it. exists

Imagine you're logged into your bank. The bank's server trusts requests from your browser because your sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request. cookieWhat is cookie?A small piece of data the browser stores and automatically sends with every request to the matching server, often used for sessions. is automatically attached to every request. Now imagine you visit a malicious site in another tab. Without any cross-originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443. restrictions, that site's JavaScript could silently do this:

fetch('https://yourbank.com/transfer', {
  method: 'POST',
  body: JSON.stringify({ to: 'attacker', amount: 5000 }),
  credentials: 'include'   // your session cookie goes along for the ride
});

The bank sees a valid session cookie and processes the transfer. You never clicked anything. This attack is called Cross-Site Request ForgeryWhat is csrf?Cross-Site Request Forgery - an attack where a malicious website tricks your browser into sending a request to another site where you're logged in. (CSRF), and the same-origin policyWhat is same-origin policy?A browser security rule that prevents JavaScript on one origin from reading responses from a different origin. is one of the main defenses against it. CORS is the mechanism that lets servers selectively relax that policy for trusted origins.

The browser enforces CORS, not the server. If you bypass it with a server-side proxy (which is fine in development), you're not "fixing" CORS, you're routing around it. The distinction matters for understanding what protection you're actually getting.
03

How the browser checks cross-originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443. requests

Simple requests

For "simple" requests (GET or POST with only basic headers and form-like content types), the browser sends the real request immediately but attaches an Origin header:

GET /api/users HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000

The server must respond with the right header or the browser will block JavaScript from reading the response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000

If the header is missing or the value doesn't match, the browser suppresses the response from your JavaScript. The request did reach the server, the server just didn't give permission to read the result.

Preflight requests

For anything more complex, PUT, DELETE, custom headers like Authorization, JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it. content type, the browser first sends a preflight OPTIONS request to ask the server what it allows:

OPTIONS /api/users HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

The server must respond with explicit permissions:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Only if that preflight succeeds will the browser send the actual request. Access-Control-Max-Age caches the preflight result so the browser doesn't repeat it for every request, 86400 seconds is one day.

04

What AI generates vs what you need

When AI generates CORSWhat is cors?Cross-Origin Resource Sharing - a browser security rule that blocks web pages from making requests to a different domain unless that domain explicitly allows it. configuration, it almost always takes the path of least resistance. Here is what that looks like compared to what production actually requires:

What AI generatesWhat production needsWhy it matters
origin: '*'Explicit origin allowlistWildcard breaks credentials and opens you to CSRF
No credentials optioncredentials: true with explicit originAuth cookies won't cross origins without both
Single origin stringDynamic origin functionStaging, production, and local dev need different origins
No Access-Control-Max-AgemaxAge: 86400Without caching, browsers preflight every single request
CORS middleware after routesCORS middleware before all routesOrder matters, routes hit before CORS middleware get no headers
AI pitfall
When you tell AI "fix my CORS error," it will add origin: '*' almost every time. This silences the error immediately but breaks the moment you add credentials: 'include' for authentication cookies. Browsers refuse the wildcard-plus-credentials combination as a security rule. Always specify your actual frontend origin.
05

Configuring CORSWhat is cors?Cross-Origin Resource Sharing - a browser security rule that blocks web pages from making requests to a different domain unless that domain explicitly allows it. in Express

The easiest production-ready approach is the cors npm package with a dynamic originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443. function:

const cors = require('cors');

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

app.use(cors({
  origin: (origin, callback) => {
    // Allow requests with no origin (mobile apps, server-to-server)
    if (!origin) return callback(null, true);

    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true  // allows cookies to cross origins
}));

When you set credentials: true, you must also specify an explicit origin, you cannot use origin: '*' with credentials. Browsers will reject it as a security violation.

AI pitfall
When AI generates a CORS config with origin: true (which reflects the requesting origin back), it effectively behaves like a wildcard, any origin is accepted. This is convenient for development but dangerous in production because it defeats the purpose of CORS entirely. Use an explicit allowlist instead.
06

Debugging CORSWhat is cors?Cross-Origin Resource Sharing - a browser security rule that blocks web pages from making requests to a different domain unless that domain explicitly allows it. errors

When you see a CORS error in the browser console, the first step is to look at the Network tab in DevTools. Find the failing request and check its Response Headers. You'll usually see one of these situations:

SymptomLikely causeFix
No Access-Control-Allow-Origin header at allCORS middleware not runningAdd cors() before your routes
Header present but wrong valueOrigin mismatch in allowlistAdd the requesting origin to your list
Preflight returns 404 or 405OPTIONS not handledEnsure CORS middleware handles OPTIONS
Works without credentials but not withWildcard + credentials conflictUse explicit origin, not *
A CORS error in the browser does not mean the request never reached your server. For simple GET requests, the server often processed the request fully, the browser just blocked JavaScript from reading the response. Check your server logs alongside browser DevTools.
07

Development workarounds

During development, routing your frontend's APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. calls through the same dev server sidesteps CORSWhat is cors?Cross-Origin Resource Sharing - a browser security rule that blocks web pages from making requests to a different domain unless that domain explicitly allows it. entirely because both sides share an originWhat is origin?The combination of protocol, domain, and port that defines a security boundary in the browser, like https://example.com:443.:

// In Vite config - proxy /api to your backend
server: {
  proxy: {
    '/api': 'http://localhost:4000'
  }
}

Your frontend fetches /api/users, the dev server forwards it to http://localhost:4000/api/users, and the browser never sees a cross-origin request. This is safe for development but don't rely on it in production, configure proper CORS headers on your actual server.

08

Quick reference

HeaderPurposeExample value
Access-Control-Allow-OriginWhich origins can read the responsehttps://myapp.com
Access-Control-Allow-MethodsWhich HTTP methods are permittedGET, POST, PUT, DELETE
Access-Control-Allow-HeadersWhich request headers are allowedContent-Type, Authorization
Access-Control-Allow-CredentialsWhether cookies may cross originstrue
Access-Control-Max-AgeCache preflight response (seconds)86400
javascript
// Production CORS setup, what AI should have generated

const express = require('express');
const cors = require('cors');
const app = express();

const corsOptions = {
  origin: function (origin, callback) {
    if (!origin) return callback(null, true);

    const allowedOrigins = [
      'https://myapp.com',
      'https://www.myapp.com',
      'https://staging.myapp.com'
    ];

    if (process.env.NODE_ENV === 'development') {
      allowedOrigins.push('http://localhost:3000');
    }

    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

app.get('/api/users', (req, res) => {
  res.json({ users: ['Alice', 'Bob'] });
});

app.listen(4000);