CORS — Cross-Origin Resource Sharing

Chapter 5
CORS — Cross-Origin Resource Sharing
The Same-Origin Policy, preflight requests, and how to actually read and fix a real CORS error

CORS is one of the most commonly misunderstood parts of web development — not because it's conceptually difficult, but because the error message it produces in the console rarely explains what's actually wrong. This chapter covers the underlying policy CORS exists to relax, what a "preflight" request actually is, and how to read a real CORS error well enough to fix it directly rather than guessing.

The Same-Origin Policy — What CORS Exists to Relax

Browsers enforce a default security rule called the Same-Origin Policy: JavaScript running on one origin cannot read the response of a request made to a different origin, unless that other origin explicitly allows it. An "origin" is the combination of scheme, host, and port — all three have to match exactly.

URLCompared to https://osztromok.comSame origin?
https://osztromok.com/linuxDifferent path only✅ Same origin
http://osztromok.comDifferent scheme (http vs https)❌ Different origin
https://blog.osztromok.comDifferent host (subdomain)❌ Different origin
https://osztromok.com:8080Different port❌ Different origin
This is why a subdomain feels like "the same site" but isn't, to the browser
osztromok.com and blog.osztromok.com are different origins as far as the Same-Origin Policy is concerned, even though they're clearly part of the same overall website. This is exactly why Chapter 2's Domain cookie attribute exists — it's one of the few mechanisms that deliberately bridges origins within the same site.

What CORS Actually Does

CORS is a set of HTTP headers that let a server explicitly opt in to allowing requests from other origins — without it, the Same-Origin Policy blocks the response from being readable by the requesting page's JavaScript, even if the request itself technically succeeded on the server's end.

// The server includes this header in its response to allow cross-origin reads:
Access-Control-Allow-Origin: https://osztromok.com

// Or, less restrictively (use with caution — see warning below):
Access-Control-Allow-Origin: *
CORS is enforced by the BROWSER, not the server
A common misconception: the request to the server still happens, and the server still processes it — CORS only controls whether the browser lets the requesting page's JavaScript read the response. This is why CORS errors only ever show up in browser-based JavaScript (fetch, XMLHttpRequest) and never affect server-to-server requests, curl, or Postman, which don't enforce this policy at all.

Preflight Requests — The OPTIONS Request You Didn't Write

For anything beyond a simple GET/POST with standard headers, the browser sends an automatic OPTIONS request first — a "preflight" — asking the server's permission before sending the real request at all.

Browser 1. OPTIONS preflight — "can I do this?" api.other.com 2. If allowed: actual GET/POST request Real request sent
The preflight happens automatically, invisibly to your code — you never write the OPTIONS request yourself

What triggers a preflight

  • Non-GET/HEAD/POST methods — PUT, DELETE, PATCH always trigger a preflight.
  • Custom headers — anything beyond the small set of "CORS-safelisted" headers (like a custom Authorization or X-Custom-Header) triggers a preflight.
  • Content-Type other than the simple setapplication/json (extremely common for APIs) triggers a preflight; text/plain doesn't.
// The server's response to the preflight OPTIONS request needs these:
Access-Control-Allow-Origin: https://osztromok.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

Reading Real CORS Error Messages

No 'Access-Control-Allow-Origin' header is present
The server's response didn't include the header at all — either CORS isn't configured server-side, or the request hit an error page (404, 500) that doesn't have CORS headers configured either.
Fix: add the header server-side; check the request isn't actually failing for an unrelated reason first.
has been blocked by CORS policy: Response to preflight request doesn't pass
The OPTIONS preflight itself got an unsatisfactory response — often the server doesn't handle OPTIONS requests at all, or restricts allowed methods/headers too narrowly.
Fix: ensure the server explicitly handles OPTIONS and returns the right Allow-Methods/Allow-Headers.
The value of the 'Access-Control-Allow-Origin' header... must not be the wildcard '*' when credentials mode is 'include'
A specific, common combination: sending cookies/credentials cross-origin requires an exact origin in the header — the wildcard isn't allowed in that case.
Fix: set Access-Control-Allow-Origin to the exact requesting origin, and Access-Control-Allow-Credentials: true.
Never set Access-Control-Allow-Origin: * on an endpoint that uses cookie-based auth
Combined with credentialed requests, a wildcard origin would let literally any website read authenticated responses on a user's behalf — exactly the kind of cross-site request forgery scenario covered conceptually in this course's earlier SameSite chapter. Browsers actively prevent this exact combination (the error above), which is a safety net, not a bug to work around.

Debugging a Real CORS Issue, Step by Step

  • Check the Network tab first, not just the console error. Find the actual failed request, check if there's a preceding OPTIONS request, and look at its response headers directly.
  • Confirm the request even reached the server successfully. A 404 or 500 response often shows up looking like a CORS error in the console, even though the real problem is unrelated to CORS at all.
  • Match the origin exactly. http:// vs https://, or a trailing slash difference, is enough to make an otherwise-correct Allow-Origin header not match.

Chapter 5 Quick Reference

  • Same-Origin Policy — scheme + host + port must all match for two URLs to share an origin
  • CORS — server-sent headers that explicitly opt in to cross-origin reads; enforced by the browser, not the server
  • Preflight (OPTIONS) — automatic browser check before non-simple requests (custom methods/headers, JSON content-type)
  • Access-Control-Allow-Origin/Methods/Headers — the core response headers a server needs for CORS to work
  • Wildcard (*) + credentials don't mix — browsers block this combination deliberately, for good security reasons
  • Debug via the Network tab — check for a preflight, confirm the request actually succeeded server-side, verify exact origin matching
  • Next chapter: security headers from the browser's perspective — CSP, HSTS, X-Frame-Options