Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Networking › HTTP

CORS

Why a request works in Postman but is blocked only in the browser

By default, a browser does not let a script on a web page read a response from a server at a different origin. The block is lifted only when the server says in a response header that this origin is allowed.

Checked against real execution · Ran 24 combinations with two test servers and a real browser

Concepts in this lab

CORS and the same-origin policy
The same-origin policy is a browser security policy that stops a script from one origin (the combination of scheme, host, and port) from reading responses from another origin. CORS is a way to relax this restriction: the server uses response headers to say that it allows access from specific origins. The browser applies this policy, so it does not apply to calls between servers or to calls made with tools.
Preflight request
A check request that the browser sends first, with the OPTIONS method, before it sends the actual request to another origin. It asks the server in advance whether the server allows that origin, method, and headers, and the browser sends the actual request only if it receives a response that allows them. It occurs for requests such as those that send a JSON body or carry custom headers.
HTTP methods
The part of an HTTP request that indicates the action the request wants to perform on a resource. GET means read, POST means create or a processing request, PUT means replace the whole resource, PATCH means modify part of it, and DELETE means delete. Use each one according to its meaning so that HTTP behavior such as caching and retries works as expected.
Cookie
A small piece of data that a server sends in a response. The browser stores it and automatically sends it back with every request to the same site. HTTP does not remember state between requests, so this data is used to keep information that must carry over, such as a login state. Because it is stored in the user's browser, you must use it on the assumption that the user can read or change its contents.

Try these

Running with the default settings

  1. The origin of the page is https://shop.example, and the origin of the API is https://api.shop.example. The hosts differ, so the origins differ. The browser first decides whether this is a kind of request that it may simply send.
  2. This is not a simple request (the Content-Type is application/json). Before sending the actual request, the browser first asks the server with the OPTIONS method whether it may send a request like this. The script did not ask for this request. The browser sends it on its own.
  3. The server's response does not meet the conditions (The response has no Access-Control-Allow-Origin header). The browser does not send the actual request at all, and reports a network error to the script. The order-handling code on the server did not run.

Result of this run

Code

// Script on the page https://shop.example
fetch('https://api.shop.example/orders', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(order)
});

// Server: no CORS settings

Assumptions of this model

Where the server allows the origin, it is assumed to allow all methods and headers as well. The conditions for a simple request (the method is GET, HEAD, or POST, there are no extra headers, and the Content-Type is a form submission type or text/plain), the preflight request, and the rules when cookies are included follow the explanations in the Fetch standard and the MDN documentation. The 24 combinations of requests to a different origin (4 kinds of request × cookies included or not × 3 server settings) were run with two local test servers and a real browser (Chromium-based). Whether a preflight request was sent, whether the actual request reached the server, and whether the script received the response all matched the model.

Previous lab: Content negotiation and compression · Next lab: Polling, SSE, and WebSocket

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these