Writing

What Is a CORS Error and Who Does It Actually Protect? (Same-Origin Policy & Preflight OPTIONS)

Why does an API that works flawlessly in Postman or cURL throw a red "Blocked by CORS policy" error in the browser? Understand the Same-Origin Policy, Preflight OPTIONS requests, and Access-Control-Max-Age caching.

Furkan ÖzayOctober 30, 2026 · 2 min read
czCZAY.DEV // EDITORIALEN
WEB GÜVENLIĞI

#Web Güvenliği · 2 MIN READ

FURKAN ÖZAYVOL. 2026 // #FB4E

Every web developer has stared at this infamous red error in the browser console:

text
Access to fetch at 'https://api.example.com/v1/transfer' from origin 'http://localhost:3000' has been blocked by CORS policy

The knee-jerk reaction is often to slap Access-Control-Allow-Origin: * onto the backend and move on. But first, ask yourself: Why does the exact same request work in Postman or cURL without any error, yet fail inside the browser?

Because CORS (Cross-Origin Resource Sharing) is not a server firewall—it is a browser security mechanism that protects the user.

1. Why Does the Same-Origin Policy (SOP) Exist?

By default, browsers enforce the Same-Origin Policy (SOP). Two URLs share the same origin only if their scheme (https), host (czay.dev), and port (443) match exactly.

Imagine a world without the Same-Origin Policy:

  1. You log into your bank (https://bank.com), and your session cookie is stored in the browser.
  2. In another tab, you open a malicious game or news site (https://evil.com).
  3. JavaScript running on evil.com could silently send a fetch('https://bank.com/api/account') request in the background using your browser's cookies and read your private financial data.

SOP blocks evil.com from reading responses from bank.com.

2. How CORS Opens a Controlled Door

In modern web apps, your frontend (https://app.company.com) and backend API (https://api.company.com) often live on different origins. For the browser to permit this legitimate cross-origin call, the server must explicitly return an allowlist header:

http
Access-Control-Allow-Origin: https://app.company.com
Access-Control-Allow-Credentials: true

3. Hidden Preflight OPTIONS Requests & Access-Control-Max-Age

Whenever your frontend sends a PUT, DELETE, or Content-Type: application/json request, the browser first sends a hidden OPTIONS (Preflight) request to ask the server for permission before sending the actual payload.

Firing a preflight request before every single API call doubles your network round-trip latency! You can eliminate repeated preflight requests by instructing the browser to cache the permission:

http
Access-Control-Max-Age: 86400

With Access-Control-Max-Age: 86400, the browser caches the CORS preflight response for up to 24 hours, skipping redundant OPTIONS round-trips and significantly speeding up your frontend.

Read next