Tags: web-dev concept

The Same-Origin Policy

Date: 2026-08-16


A page can’t read data from a different origin. It’s the boundary the entire web security model rests on, and CORS, Content Security Policy and cookie rules are all either exceptions to it or reinforcements of it.


What it is

The same-origin policy prevents a document loaded from one origin from reading data belonging to another.

An origin is the triple: scheme + host + port. All three must match exactly.

https://shop.example.com/products          ← the page

https://shop.example.com/api/basket    ✓  same origin
http://shop.example.com/api            ✗  different scheme
https://www.example.com/api            ✗  different host
https://shop.example.com:8443/api      ✗  different port

Subdomains are different origins. example.com and www.example.com are different origins. This surprises people constantly.

Why it exists

Without it, any site you visited could make a request to your bank with your cookies attached and read the response.

you visit evil.com
  → its JavaScript requests https://bank.com/balance
  → your bank cookies are attached automatically  ← this happens regardless
  → evil.com reads your balance                   ← THIS is what SOP blocks

The crucial detail: the request is often still sent. The browser blocks reading the response, not always the sending. That asymmetry is why CSRF exists as a separate problem — a request that changes state doesn’t need its response read to do damage, which is what SameSite cookies address. See Cookies and Common Vulnerabilities.

What it blocks and doesn’t

ActionCross-origin allowed?
fetch() / XMLHttpRequest reading a responseNo, unless the server opts in via CORS
Reading another frame’s DOMNo
Reading localStorage, cookies, IndexedDBNo
Loading an image via <img>Yes — but you can’t read its pixels from a canvas
Loading a script via <script>Yes — and it runs with your origin’s privileges
Loading a stylesheet, font, or mediaYes
Submitting a formYes
Embedding in an <iframe>Yes, unless the target refuses

The <script> row is the important one. A cross-origin script isn’t sandboxed — it executes as if you wrote it, with full access to your DOM, your cookies, and your storage. That’s why every third-party tag is a trust decision rather than a performance decision. See Third-Party Scripts and Subresource Integrity.

The sanctioned exceptions

Each is a deliberate hole with its own opt-in:

  • CORS — the server sends headers permitting specific origins to read its responses
  • postMessage — explicit, structured messaging between frames, where both sides verify the origin
  • document.domain — a legacy relaxation for subdomains, now deprecated and disabled by default [CHECK: current status per engine]
  • JSONP — the historical hack of loading data as a script. Obsolete and dangerous, since the response executes with your privileges

Where you meet it

  • “Blocked by CORS policy” in the console. Almost always means the server hasn’t opted in, not that your fetch is wrong — CORS
  • Canvas tainting. Drawing a cross-origin image onto a canvas makes toDataURL() and getImageData() throw, unless the image was loaded with crossorigin and the server permits it
  • Iframe access failing on a payment or checkout frame. Correct behaviour — use postMessage
  • Cookies not shared between www. and the apex domain, or between a Shopify checkout on a different host and your storefront — Checkout Instrumentation Constraints
  • Analytics on subdomains. shop.example.com and blog.example.com are separate origins for storage; a cookie must be set with Domain=example.com to be shared, and doing that exposes it to every subdomain — Identity Stitching
  • Site is broader than origin — roughly the registrable domain, so a.example.com and b.example.com are the same site but different origins. SameSite cookies use site; the same-origin policy uses origin. Mixing the two up is the cause of a lot of confused debugging
  • Content Security Policy restricts what your own page may load. Same-origin policy restricts what it may read from elsewhere. They’re complementary, not alternatives