◐𝕏XGitHubLinkedInRSSGuestbookArchives
← Back
June 21, 2026

Developers Don't Understand CORS: Why It Still Matters in 2026

A deep dive into why CORS remains one of the most misunderstood web security concepts, what the HN community gets wrong, and what builders need to know.

In 2019, a developer named Kevin Foster published a short post titled "Developers don't understand CORS." It resurfaced on Hacker News recently, scoring 152 points and sparking 67 comments that perfectly proved his thesis. Even the article itself got dragged for inaccuracies, and the comment section devolved into a battleground of half-right explanations. Seven years later, CORS still bamboozles developers.

Why CORS Is Still Misunderstood

Foster's article uses a concrete example: Zoom's web client opening a localhost web server to handle a phone call. He argues that developers often treat CORS as a security feature that protects the backend, when in fact it protects the user's browser from unintended data leaks. He walks through how misconfiguring CORS—or misunderstanding its purpose—can lead to real vulnerabilities.

One of his key points: the Access-Control-Allow-Origin header does not block requests from other origins. It merely tells the browser whether to share the response with the requesting page. A malicious site can still send a POST request to your API, including cookies. The browser just won't let the malicious site read the response. That distinction—CORS controls reading, not sending—is where most developers go wrong.

The HN Debate Proves CORS Confusion

The thread is a case study in Dunning-Kruger. On one hand, commenters nitpicked Foster's own language. For example, Foster wrote:

The webserver listening in on localhost:19421 should implement a REST API and set a Access-Control-Allow-Origin header with the value https://zoom.us. This will ensure that only Javascript running on the zoom.us domain can talk to the localhost webserver.

One commenter corrected: "No, that does not do that. JavaScript from any other website can still talk to localhost:19421 just the same. CORS doesn't restrict anything, it loosens the default set of restrictions (ignoring preflight requests for now and assuming we're talking just about simple requests)."

That comment is spot-on. CORS is opt-in for cross-origin reads, not a firewall. Yet another commenter lamented: "I think this is legitimately the least informed HN comment section I've ever seen. Entirely proving the author's point." Others admitted they never really get it: "I'm one of them. CORS is THE topic that I have to get a refresher for periodically."

The consensus, if there is one, is that backend developers often configure CORS without understanding the threat model. As one said: "Part of this is that backend developers usually have to configure CORS and it's not an access privilege protection. From the point of view of the backend it doesn't seem to matter."

CORS vs SOP: The Core Distinction

Foster's article is imperfect but useful. The real problem isn't that developers don't know the spec—it's that they confuse two very different things:

  1. Same-Origin Policy (SOP): The browser's default rule that blocks cross-origin reads. It's a security boundary.
  2. CORS: A mechanism to relax SOP. Without CORS headers, the browser enforces SOP. With CORS, the server opts into allowing cross-origin reads.

Most tutorials show you how to add Access-Control-Allow-Origin: * to get past a "CORS error" during development. But they rarely explain that this header does nothing to prevent a malicious site from sending a request with cookies. If you rely on CORS to protect your API, you'll get burned. Authentication should be enforced server-side using tokens that are not automatically sent (like Authorization headers), not session cookies that browsers attach automatically.

Where Foster's article stumbles is in implying that the header "ensures only Javascript from zoom.us can talk to the localhost webserver." That's false. As the commenter noted, any origin can send a request; the header only controls whether the browser exposes the response. But the overall point—that you must understand CORS to secure a web app—stands.

The other overlooked aspect is preflight requests. Most developers know they exist, few understand when they're triggered. Simple requests (GET, POST with certain content types) don't trigger preflight. Others require an OPTIONS handshake. This leads to surprises when you switch from application/x-www-form-urlencoded to application/json and suddenly your POST breaks. For a full breakdown, see the MDN guide on CORS preflight.

Practical CORS Configuration for Your API

If you're building a web API, here's what you actually need to know:

  • CORS is a browser-only concept. It does not protect your API from server-to-server requests, curl, or mobile apps.
  • Use CORS to whitelist origins for read access, not for authorization. Your server must still authenticate every request.
  • Avoid relying on cookies for cross-origin authentication unless you set SameSite=None; Secure and understand the implications. Prefer the Authorization header with a bearer token.
  • Handle preflight correctly. Your server must respond to OPTIONS requests with the appropriate headers.

Here's a minimal Express example that sets CORS for a single origin:

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

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', 'https://myapp.com');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204);
  }
  next();
});

This only allows https://myapp.com to read responses. Any origin can still write (POST, PUT) and the request will hit your server regardless.

If you're a frontend developer, you need to understand that the "CORS error" you see in the console is the browser protecting your user from a potentially malicious API response. It's your job to ensure the API sends the right headers. And if you're using fetch with credentials: 'include', you cannot use a wildcard origin—you must specify exactly one.

When You Need CORS (And When You Don't)

If you work on any web application that uses a different domain for frontend and backend (e.g., app.example.com and api.example.com), you absolutely need to understand CORS. If you're a backend developer who writes APIs consumed by browsers, you need to understand it even more. If you only write server-to-server APIs or native mobile apps, you can ignore CORS almost completely—just don't accidentally copy-paste headers that do nothing for you.

The fact that this 2019 article still generates heated debate in 2025 tells you that our industry has a blind spot. CORS isn't hard—it's just poorly taught. Read the MDN article on CORS once, then bookmark it. And if you're still confused after reading this? That's okay. You're not alone. Just don't pretend you understand it until you do.


Original HN discussion: https://news.ycombinator.com/item?id=48614844
Original article: https://fosterelli.co/developers-dont-understand-cors
MDN CORS reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS

Share on Twitter
← Back to all posts