Original guides / Guide

Understand CORS with a controlled request model

Reproduce allow and deny decisions without sending requests to someone else’s server.

You will learn to

  • Explain origins and preflight
  • Separate CORS from authorization
  • Model an explicit allowlist

Before you start

HTTP request basics

Define the origin precisely

An origin combines scheme, host and port. localhost:5173 and localhost:5174 are different origins. The browser uses the requesting page’s origin to decide whether script may read a cross-origin response. A path is not part of the origin.

CORS is a browser response-reading policy, not authentication. Command-line clients are not constrained by it. A service that trusts CORS alone may still expose data to direct requests. Keep this distinction clear when debugging an API.

Why a preflight happens

Certain methods and headers trigger an OPTIONS preflight. The browser asks whether the server allows the requested origin, method and headers before sending the actual request. Adding a custom authorization header can change the behavior.

Respond with an explicitly allowed origin and appropriate allowed methods and headers. When the response varies by origin, include Vary: Origin to prevent shared caches mixing policies. Credentialed requests cannot use a wildcard Access-Control-Allow-Origin in place of a specific origin.

Common misleading fixes

mode: "no-cors" does not unlock JSON. It yields an opaque response that script cannot read like an ordinary API response. Disabling browser security or using a random public proxy hides the issue and may leak credentials.

Inspect the Origin, response headers and preflight status. A local reverse proxy makes the request same-origin from the browser’s perspective. That is a different contract from a production cross-origin request, and must be tested separately.

Reproduce a small model

This function models a two-origin allowlist. It is a simulation of the decision, not an actual browser preflight. Controlled inputs let you reason about it offline without contacting targets.

Try the allowed origin, the same hostname with another port and an unrelated hostname. Return the exact origin only when allowed; otherwise return null. Configure real origins deliberately and keep authorization independent of this classroom model.

Read the browser evidence

For a real reproduction on your own machine, serve a page and a disposable API on two different localhost ports. Ports are part of the origin. Have the API return a harmless fixed string, first without Access-Control-Allow-Origin and then with the exact page origin. Compare whether JavaScript can read the response. A request appearing in the network panel does not mean its response is readable by the page.

The activity below models the response-header decision only. It does not generate a real preflight, implement a proxy or change any server policy. Try an allowed origin and a different origin in the exercise. As an extension, model a wildcard with credentials enabled; that combination must be rejected. Do not fix a production problem by reflecting arbitrary origins while permitting credentials; define the actual clients that need access.

Try it yourself

function allowOrigin(origin) {
  const allowed = ["https://learn.example.test", "http://localhost:5173"];
  return "*";
}
console.log(allowOrigin("http://localhost:5174"));

Enable JavaScript for this interactive activity. You can read all lesson explanations above without it.

Continue exploring