Advanced

The browser gate

My fetch worked in curl but failed in the page. The browser adds a rule curl does not: a page may only read a cross-origin response if the server allows it.

What you will see

Access-Control-Allow-Origin: https://my.site

About 1 min of reading

Try It

See the same call work in curl and fail in a page, then fix it on the server.

1

curl ignores origin rules, so it works.

curl -s https://api.other/data

Before you run it, say out loud what it will print. Then open the answer.

What it prints
[{"ok": true}]
2

In a page the same fetch is blocked until the server sends the header. Fix it on the server.

Access-Control-Allow-Origin: https://my.site

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Access-Control-Allow-Origin: https://my.site
You cannot fix CORS from the page. The server must send the header. A wildcard * is the wrong reflex for a credentialed call, name the exact origin instead.

Data Flow

The browser asks the server which origins may read a response; without the Access-Control header it blocks, and custom headers trigger an OPTIONS preflight first.
1 Page sends cross-origin fetch
↓
2 Browser checks Access-Control
↓
3 Absent header means blocked
↓
4 Custom headers preflight first
Why this works
The server decides

CORS is a server policy, not a browser one. The server says which origins may read it via Access-Control-Allow-Origin. If the header is absent, the browser refuses. So the fix is always on the server, never in the page.

Preflight before custom

A request with non-simple headers or methods triggers an OPTIONS preflight. The server must allow those headers or the real call is blocked. A confusing CORS error is often a preflight that said no to one header name.

Examples

Same rule, two results

curl https://api.other/data      # works

fetch('https://api.other/data')  // blocked in a page

Same request, different result. The browser enforces an origin rule that curl ignores, because a page runs user code.

The server must opt in

Access-Control-Allow-Origin: *

That header is the opt-in. Without it the browser hides the response. The server, not the page, decides who may read it.

The preflight

OPTIONS /data
Access-Control-Allow-Headers: Authorization

A request with custom headers sends an OPTIONS check first. The server must answer it, or the real request never fires.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Signing a request
Previous Lesson
Signing a request
Next Lesson
The server calls you back
The server calls you back →