Browser security boundaries

Understand the origin boundary

Use scheme, host, and port to reason about which documents and requests can share browser authority.

The browser uses an origin as a major security boundary. An origin is the exact triple of scheme, host, and port. All three must match for two documents to share full browser authority.

That means https://app.flaviocopes.com and http://app.flaviocopes.com are different origins, because the scheme differs. https://app.flaviocopes.com and https://app.flaviocopes.com:8443 are different origins, because the port differs. The host is compared exactly, so flaviocopes.com and www.flaviocopes.com do not match either.

Sending is not the same as reading

The same-origin policy restricts one origin from reading another origin’s data. It does not stop every cross-origin request from being sent. This distinction is where most confusion starts.

// Runs on https://evil.example
// The request IS sent, and cookies for the target may ride along
fetch('https://api.flaviocopes.com/account', { credentials: 'include' })
  .then(res => res.json()) // ← the browser blocks reading this response
  .catch(err => console.log('read blocked:', err))

The fetch() above leaves the browser and hits your server. The browser only refuses to hand the response back to the calling script. An HTML form is even more permissive: it can POST to any origin and the request arrives at your server normally.

<!-- A plain form posts cross-origin with no restriction -->
<form action="https://api.flaviocopes.com/account/email" method="post">
  <input name="email" value="[email protected]">
</form>

https://app.flaviocopes.com and https://api.flaviocopes.com belong to the same company but not the same origin. A browser may send a cross-origin form while refusing JavaScript access to its response.

Saying “the browser blocks cross-origin requests” hides this important difference. Design separately for sending, reading, cookies, navigation, and other browser-controlled behavior.

Classify five URLs by scheme, host, and port, then test one cross-origin fetch() and one HTML form submission. Record whether each request was sent and whether the initiating page could read the response.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →