// cross-origin resource sharing

CORS, from first principles.

Cross-Origin Resource Sharing is the browser's rule for when one web origin may read another's response. It is widely misunderstood — and almost always, the confusion is about where the rule is enforced. This site teaches CORS by showing you the actual requests and responses, in the browser, as they happen.

https://www.corslabs.com your page api.www.corslabs.com the server request — always sent response — withheld by the browser, not the server
Free, pro-bono, and still growing. Built by Precisit. Every page in the course below runs real cross-origin requests against a live demo API and shows the browser's actual verdict — nothing is faked or simulated. The final chapters (10–12) are still on the way.
// paste the symptom, find the rule

Diagnose your error

Choose the browser whose console message you saw, then open the matching Error Gallery exhibit.

Chrome console messages

  1. No Access-Control-Allow-Origin (actual response)Access to fetch at 'http://localhost:8787/demo/echo?scenario=simple&allow=disallow&id=ccef712b-f56f-4356-8010-da3da28e8acb' from origin 'http://localhost:4321' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.TypeError; the server received the request
  2. No Access-Control-Allow-Origin (preflight response)Access to fetch at 'http://localhost:8787/demo/echo?scenario=preflight&variant=missing-acao&id=335d55ec-8eb6-4e50-96f2-9a3b3abeb251' from origin 'http://localhost:4321' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.TypeError; only the OPTIONS preflight arrived
  3. Access-Control-Allow-Origin doesn't matchAccess to fetch at 'http://localhost:8787/demo/echo?scenario=simple&allow=mismatch&id=5c1d6574-9335-4104-9b23-8d8991659d92' from origin 'http://localhost:4321' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://127.0.0.1:8787' that is not equal to the supplied origin. Have the server send the header with a valid value.TypeError; the response carried the wrong origin
  4. Multiple Access-Control-Allow-Origin valuesAccess to fetch at 'http://localhost:8787/demo/echo?scenario=simple&allow=double&id=6e25bace-c1ea-408a-985e-d2d51bdc107c' from origin 'http://localhost:4321' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4321, http://127.0.0.1:8787', but only one is allowed. Have the server send the header with a valid value.TypeError; the Fetch spec rejects a multi-value ACAO outright
  5. Wildcard ACAO turns literal under credentialsAccess to fetch at 'http://localhost:8787/demo/echo?scenario=simple&allow=wildcard&id=8d65fb85-3d0e-45bf-839d-2644657340a6' from origin 'http://localhost:4321' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.TypeError; the wildcard can never satisfy a credentialed request
  6. Access-Control-Allow-Headers doesn't cover a custom headerAccess to fetch at 'http://localhost:8787/demo/echo?scenario=preflight&variant=missing-header&id=44652446-99f9-44a2-8b9b-e3e232d2c490' from origin 'http://localhost:4321' has been blocked by CORS policy: Request header field x-corslabs-demo is not allowed by Access-Control-Allow-Headers in preflight response.TypeError; only the OPTIONS preflight arrived
  7. Access-Control-Allow-Methods doesn't cover the methodAccess to fetch at 'http://localhost:8787/demo/echo?scenario=preflight&variant=missing-method&id=b12910e3-9049-46db-bf92-195da79dd2d1' from origin 'http://localhost:4321' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.TypeError; only the OPTIONS preflight arrived
  8. Preflight answers with a non-2xx statusAccess to fetch at 'http://localhost:8787/demo/echo?scenario=preflight&variant=status-401&id=4a0d8872-b5e8-4b6d-b2df-f5180ba8136d' from origin 'http://localhost:4321' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.TypeError; the preflight itself was rejected (401, or 404 in the toggle)
  9. Preflight response is a redirectAccess to fetch at 'http://localhost:8787/demo/echo?scenario=preflight&variant=redirect&id=554c2c4f-8ab3-43ee-9bc3-4744d40bbef1' from origin 'http://localhost:4321' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.TypeError; the browser never follows a redirected preflight
  10. mode: 'no-cors' — an opaque response isn't a success(confirmed: no console error — this exhibit's fetch() resolves without throwing or logging anything)fetch() resolves — opaque: status 0, no headers, unreadable body
  11. The preflight passes, but the actual response forgets ACAOAccess to fetch at 'http://localhost:8787/demo/echo?scenario=preflight&variant=actual-missing-acao&id=a70b0111-60a7-4032-9987-604ca3c9adc3' from origin 'http://localhost:4321' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.TypeError, after both round trips arrived — the two-checkpoint surprise
  12. A response header is present but not exposed to JavaScript(confirmed: no console error — this exhibit's fetch() resolves without throwing or logging anything)fetch() resolves, body readable, but X-Corslabs-Demo is invisible to JS
// start where you are

What's your situation?

Pick the description closest to your problem for a suggested starting page.