Dev Learning Lab

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

Fundamentals › Security

Origins

What is an origin, and why does the browser tell different origins apart?

An origin is the combination of three parts at the start of an address (URL): the scheme (https or http), the host (the site name), and the port (a number). If any one of the three differs, it is a different origin, and the path and query that come after are not part of the origin. The browser blocks a script running inside a page from reading the response to a request sent to another origin. This rule is the same-origin policy. In this lab, the code box lists the address of the page that is open and the address that the script of that page requests. You change the requested address one part at a time and see whether the browser treats the origin as the same, whether the request arrives at the server, and whether the script reads the response. The code box is not a program but two addresses. The way a server allows reading (CORS) is not covered in this lab.

Partly checked against real execution · The origins of the five addresses were calculated with Node.js 24.18.0 URL, and whether the response is read and whether the request arrives at the server were checked on the PC used to write this lab with Chrome (run without a window) and a 127.0.0.1 test server, with differences made in port, host, and scheme. The shop.example addresses on the screen themselves and all the rules of the browser's decisions were not run and were checked against the documentation

Concepts in this lab

Origin
An origin is the combination of three parts at the start of an address (URL): the scheme, the host, and the port. The origin of https://shop.example/cart has the scheme https, the host shop.example, and the port 443, and it is written as https://shop.example. If an address has no port, the default port of the scheme is used (443 for https, 80 for http), and when an origin is written down, the default port is left out (RFC 6454 sections 4 and 6). Two origins are the same origin only when all three parts are the same, and if even one differs, they are different origins (RFC 6454 section 5). The path and query are not part of the origin, so addresses that differ only in the path are the same origin (MDN).
Scripts in web pages
A script in a web page is JavaScript code that is put in with the <script> element of an HTML page and that the browser which received the page runs inside that tab (MDN). A script can send an HTTP request to a server with the fetch() function, receive the response, and change the page. When the server responds, fetch() returns a Response object that holds the response, and it fails when something like a network error happens (MDN Using the Fetch API). When the origin of the requested address differs from that of the page, the browser decides whether to give the response to the script.
Scheme and default port
The scheme is the name at the very beginning of a URL, up to the first :, and it tells which rules to talk by for this URL. In http://localhost/hello, http is the scheme, and it begins with a letter (RFC 3986 section 3.1). A scheme can also define a default port (section 3.2.3), so the default port of http is 80, and if the URL does not write a port, the client connects to port 80 (RFC 9110 section 4.2.1). Without a scheme, it cannot be known what symbols such as : and / divide, so according to the URL standard (WHATWG), the text is not read as an address unless there is another address to use as a base. If there is a : as in localhost:8080/hello, localhost may even be read as the scheme.
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.

Try these

Running with the default settings

  1. When you type the address https://shop.example/cart in the address bar of the browser, the browser gets this page from the server and shows it. This page contains a script (a program that the browser runs inside the page). The browser decides the origin of the page from the first three parts of the address. The scheme (https) is which rules are used to communicate, the host (shop.example) is which site, and the port is a number that points to a program on one computer. The port is not written in the address, so the default port of https, 443, is used. The combination of these three is the origin, and the origin of this page is https://shop.example (the default port is not written).
  2. The script of the page is about to send a request to https://shop.example/api/items. The browser decides the origin of this address in the same way. The scheme is https, the host is shop.example, and the port is not written in the address, so the default port of https, 443, is used. The part after the host, /api/items (path), is not part of the origin, so it is ignored. The origin of this requested address is https://shop.example.
  3. The browser compares the origin of the page with the origin of the requested address, three parts one by one. Scheme: page https, request https → same. Host: page shop.example, request shop.example → same. Port: page 443, request 443 → same.
  4. All three are the same, so it is the same origin. A different path or query in the address does not affect the origin.
  5. The browser sends this request to the server. The request in this lab is an ordinary request (GET) by which the script fetches content, and it goes to the same origin. The request arrived at the server.
  6. The server handles the request as usual and returns a 200 response with the body "3 items".
  7. The browser checks the response. The requested address has the same origin as the page, so the browser hands the response to the script.
  8. The script receives the response and reads the status 200 and the body "3 items".

Result of this run

Code

# The page that is open now
https://shop.example/cart
# The address that the script of that page requests
https://shop.example/api/items

Assumptions of this model

The code box is not a program that was run. It lists the address of the page that is open and the address that the script requests, and shop.example is a reserved example domain, so there is no real server behind it. So we checked in two layers. First, we calculated the origins of the six addresses on the screen (one page and five requested addresses) with the URL standard implementation of Node.js 24.18.0 (new URL(...).origin), and checked against the model whether they are the same origin and which of the scheme, host, and port differs. The same calculation also showed that the origin of https://shop.example:443/x comes out as https://shop.example, the same as for an address with no port written (the default port is left out), and that addresses with a different path and query have the same origin. Second, whether the browser hands the response to the script was checked by running Chrome 155 (HeadlessChrome) on the PC used to write this lab without a window (--headless=new --dump-dom). We started a test server on 127.0.0.1 that adds no CORS headers (on a free port), and made the script of the page that the server sent request the addresses below with fetch and write the result into the page. For the same origin, we requested /api/items of the same server; for only the path and query differing, /other?x=1 of the same server; for a different host, the name localhost on the same computer and the same port (the page was on 127.0.0.1); for a different port, a server on another port; and for a different scheme, we made an https request (with a self-signed certificate) to a server that takes http and https separately on the same port. In all five combinations the request arrived at the server; for the two same-origin ones the script read the status 200 and the body, and for the other three the script received a TypeError (Failed to fetch) and could not read it. The addresses in this experiment made the same differences as the addresses on the screen, but they are not the addresses on the screen themselves. The scheme experiment requested an https address from an http page, which is the opposite direction of the example on the screen (an http address from an https page), so we also ran the same direction as the screen (an http address from an https page) separately and confirmed that the script could not read it and that the request arrived at the server. The scheme example on the screen differs in the port too (80 for http, 443 for https) along with the scheme, but the Chrome experiment made only the scheme differ. A request to the same origin had no Origin header, and a request to another origin had an Origin header, but the screen does not cover this. When we requested a port where the server was off, the error the script received was the same TypeError (Failed to fetch) as in the blocked case, and when the server of the other origin wrote the origin of the page in Access-Control-Allow-Origin in its response, the script read the body. These are behaviors observed in this Chrome version, and we did not run other browsers or other versions. What we checked against the documentation is sections 3.2, 4, 5, and 6 of RFC 6454 (an origin is three values, the scheme, host, and port; if there is no port, the default port of the scheme is used; two origins are the same only when all three values are the same; and when an origin is written down, the default port is left out), sections 4.2, 4.4, and 4.7 of the WHATWG URL Standard (the default ports of http and https are 80 and 443, a default port is stored as null in a URL, and the origin of an http or https URL is a value made of the scheme, host, and port), the page Same-origin policy of MDN (addresses that differ only in the path are the same origin, reading from another origin is mostly blocked, the way to allow reading is CORS, and the purpose is to keep a script of a malicious page from reading the content of webmail you are logged in to), and the page Using the Fetch API of MDN (if the server of another origin does not send the allowing header, the browser does not share the response with the caller). The order in which the browser checks the allowing mark after receiving the response of the server, and the purpose of "protecting the content of other sites you are logged in to", follow the explanation in the documentation, and we did not run whether cookies actually go along. What the screen leaves out is the process of getting the page, the headers of the request and the response, and the interpretation of the response body. We covered only one ordinary GET request with no special headers. The case where the browser first asks the server before sending the request (a preflight request), requests that carry cookies, and the way a server allows reading (CORS) are covered in the in-depth lab "CORS", attacks aimed at origins in the in-depth lab "Three web attacks", and putting a page of another site into a frame in the in-depth lab "Clickjacking and open redirects". We also did not cover the fact that loading images and script files from another origin (embedding) is usually allowed, or the remaining origin rules (cookies, storage, and so on).

Previous lab: Encryption · Next lab: Input and injection

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these