Skip to content
Commitpedia
Log in Join & edit

CORS explained: why the browser blocks your request

CORS is the browser rule that decides whether JavaScript on one origin may read a response from another. The server opts in with Access-Control-* headers; the browser enforces it.

Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?

Edit history of CORS explained: why the browser blocks your request: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
2 min

Browsers follow the same-origin policy: a script running on one origin may not read responses from another origin unless that other server allows it. CORS (Cross-Origin Resource Sharing) is the mechanism a server uses to say “yes, this origin may read my responses.”

What counts as an origin

An origin is the combination of scheme, host, and port. http://localhost:5173 and http://localhost:3000 are different origins because the ports differ. So are http://example.com and https://example.com, and example.com and api.example.com.

Who enforces it

CORS is enforced by the browser, not by the server. That has two surprising consequences. First, curl, Postman, and backend code never see CORS errors, because they do not apply the policy. Second, the request often does reach the server; the browser just refuses to hand the response to your JavaScript. CORS protects users’ data in the browser. It is not a way to stop other servers from calling your API.

Simple requests

For a so-called simple request, such as a GET or a POST with a form-style content type and no custom headers, the browser sends the request with an Origin header and checks the response:

GET /api/items HTTP/1.1
Host: localhost:3000
Origin: http://localhost:5173

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:5173
Vary: Origin
Content-Type: application/json

If Access-Control-Allow-Origin matches the page’s origin (or is *), the script can read the response. Otherwise you get a CORS error in the console.

Preflight requests

Anything beyond a simple request, such as Content-Type: application/json, an Authorization header, or methods like PUT and DELETE, triggers a preflight. The browser first sends an OPTIONS request asking for permission:

OPTIONS /api/items HTTP/1.1
Origin: http://localhost:5173
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization

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

Only if the preflight succeeds does the browser send the real request. Access-Control-Max-Age lets the browser cache the answer for a while (browsers apply their own upper limit). A common bug is a server that requires authentication on every route, including OPTIONS, so the preflight fails with 401 before the real request is ever sent.

Cookies and credentials

By default, cross-origin fetch() does not send cookies. To include them, the client sets credentials: "include" and the server must respond with Access-Control-Allow-Credentials: true and a specific origin. The wildcard * is not allowed together with credentials.

Key takeaways

  • The fix for a CORS error is almost always on the server or in a development proxy, not in your fetch() call.
  • Allow specific origins rather than * for anything private, and send Vary: Origin when the header value depends on the request.
  • mode: "no-cors" does not bypass CORS. It gives you an opaque response whose body and status you cannot read.

For the exact console messages and step-by-step fixes, see Fix: Access to fetch at ‘…’ from origin ‘…’ has been blocked by CORS policy.

Did this work for you?

No votes yet. Be the first.

If it did not work, the report form below tells the editors what went wrong.

See also

  • 0018 Promises and async/await explained A promise is a placeholder for a value that arrives later. async/await lets you write code that waits for promises as if… Intermediate · Explains an idea · Any system Article Async code 15 Oct 2026
  • 0019 fetch with timeout, retries and proper error handling A reusable fetchJson helper for Node 20+ and browsers that adds a timeout, retries with backoff for temporary failures, and clear errors… Intermediate · Step by step · Any system Script Async code · Making requests 15 Oct 2026
  • 0023 Run async tasks with a concurrency limit A small mapLimit helper that runs an async function over many items with at most N in flight, keeps result order, stops… Advanced · Step by step · Any system Script Async code 15 Oct 2026
  • 0081 How an HTTP request works: methods, headers, and body An HTTP request is a short text message a client sends to a server: a method and path, some headers, and sometimes… Beginner · Explains an idea · Any system Article Making requests 15 Oct 2026
  • 0083 Test a JSON API with curl A runnable bash script showing the curl commands you need most when testing an API: GET, POST JSON, custom headers, Bearer tokens,… Beginner · Step by step · Linux, macOS, Windows Script Authentication · Making requests 15 Oct 2026
  • 0084 Tiny JSON echo server for testing HTTP clients A standard-library Python server that echoes back the method, path, query, headers, and body of every request, and can return any status… Intermediate · Step by step · Any system Script Files & automation · Making requests 15 Oct 2026

Repos in the wild

  • Requests: HTTP for humans psf/requests Requests is the classic Python library for calling web APIs and downloading pages, with a simple API for GET and POST requests, headers, JSON and sessions. GitHub
  • Axios axios/axios A promise-based HTTP client for browsers and Node.js with interceptors, automatic JSON handling, request cancellation and a consistent API in both environments. GitHub
  • curl and libcurl (curl/curl) curl/curl The source of curl, the command-line tool for transferring data with URLs, and libcurl, the C library behind it that many other programs use for HTTP. GitHub

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Dominik +3,282 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?