Browsers key some behaviour to an "origin" and other behaviour to a "site". How does a browser compute a site, how does that differ from an origin, and name something keyed to each.
answer
- one is coarser than the other
- public suffix plus one label
- browsers ship a curated list
- subdomains: same site, different origins
- scheme counts, port does not
basics
~20 sAn origin is scheme+host+port; a site is the registrable domain — the public suffix plus one label, computed from the Public Suffix List — plus the scheme under schemeful same-site. Web storage is keyed by origin; cookie same-site decisions are keyed by site.
solid answer
~40 sAn **origin** is scheme + host + port, compared exactly. A **site** is coarser: the *registrable domain*, meaning the public suffix plus one more label, worked out from the Public Suffix List — and current browsers apply *schemeful* same-site, so the scheme counts too while the port never does. So `https://app.example.com` and `https://api.example.com` are two origins but one site; `https://example.com` and `http://example.com` are one host but two sites under the schemeful rule. The list matters because the suffix is not just the TLD: `example.co.uk` is a registrable domain because `co.uk` is a public suffix, and `alice.github.io` and `bob.github.io` are *different* sites because `github.io` is listed. As for keying: `localStorage`, `sessionStorage` and IndexedDB are partitioned by origin, while whether a cookie counts as first-party on a given navigation is a site-level judgment.
go deeper
Know that a site is looser than an origin: subdomains of one domain are the same site but different origins. Be able to say that storage is per-origin, so two subdomains do not share it.
Explain the registrable-domain computation — public suffix plus one label, read from the Public Suffix List — and that current browsers fold the scheme in but never the port. Give one origin-keyed and one site-keyed example.
Reason about the blast radius: any host under your registrable domain is site-adjacent to production, so a stale staging subdomain or a customer-controlled vanity host is a real exposure for anything decided at site granularity.
Decide the domain layout for a platform. Know when a tenant needs its own registrable domain rather than a subdomain, when submitting your domain to the Public Suffix List is the right move, and what that costs in cookies and single sign-on.
## Two granularities, deliberately The web has two units of grouping and they do different jobs. The **origin** is the strict one: scheme, host and port, compared component by component. It is the boundary for scripting and for storage — the thing the same-origin policy enforces. The **site** is the loose one: a family of hosts under one registrable domain, usually assumed to be run by one organisation. It exists because some questions — "is this navigation staying within one company's property?" — cannot be answered usefully at origin granularity, since a single company routinely spreads itself over dozens of subdomains. ## How a site is computed Start from the host and find its *public suffix*: the longest suffix under which anyone may register a name. Add exactly one label to the left of it, and you have the **registrable domain**, also written eTLD+1 (effective top-level domain plus one). The catch is that a public suffix is not simply the TLD. `co.uk`, `com.au`, `github.io`, `vercel.app` and thousands of others are public suffixes, because registration below them is open to unrelated parties. Browsers do not derive this — they ship the **Public Suffix List**, a curated data file, and consult it. ``` host = app.shop.example.com -> suffix com -> site example.com host = shop.example.co.uk -> suffix co.uk -> site example.co.uk host = alice.github.io -> suffix github.io -> site alice.github.io ``` That third line is the one that surprises people: two project pages on the same hosting domain are different sites, which is the entire point of listing the domain. Modern browsers also apply **schemeful same-site**: the scheme is folded into the comparison, so `http://example.com` and `https://example.com` are cross-site even though the host is identical. The port is never part of a site. ## Origin versus site, side by side | | origin | site | |---|---|---| | host | exact match | same registrable domain | | scheme | must match | must match (schemeful same-site) | | port | must match | ignored | Every same-origin pair is same-site. The reverse is not true, and that gap — same site, different origin — is where most real-world confusion lives. ## What is keyed to which **Origin-keyed:** `localStorage`, `sessionStorage` and IndexedDB partitions; the `Origin` request header; the target-origin check on cross-document messaging; the same-origin policy's own DOM-access decisions. Two subdomains of one company get *separate* storage and cannot script each other, however much they feel like one product. **Site-keyed (or host-keyed):** cookies are the big one. A cookie is scoped by host and path, is shared across ports, and whether it counts as first-party on a particular request is decided at site granularity. Browsers also partition several caches and storage areas by the *top-level* site, so the same embedded third party loaded under two different parent sites sees two different partitions. ## Why this bites in practice The most common surprise is a team that assumes "our subdomains share everything". They do share cookies if a cookie is deliberately scoped to the parent domain, but they do **not** share `localStorage`, `sessionStorage` or IndexedDB, and script on one cannot read the other's DOM. A token stashed in `localStorage` on `app.example.com` simply does not exist on `checkout.example.com`. The second surprise runs the other way. Because a site spans every subdomain, any host under your registrable domain is site-adjacent to your main app — a forgotten staging box, a marketing microsite, a customer-controlled vanity subdomain. That host cannot script your origin, but it is inside your site for anything decided at site granularity, including domain-scoped cookies. Getting an untrusted service onto its own registrable domain, rather than a subdomain of yours, is a meaningfully stronger separation than getting it onto its own origin. The third is the PSL itself. If you operate a platform that hands out subdomains to independent customers, submitting your domain to the Public Suffix List is what makes each customer subdomain its own *site* rather than all of them sharing yours.
- Two subdomains of one company share cookies scoped to the parent domain. Do they share localStorage too?No. Web storage — `localStorage`, `sessionStorage` and IndexedDB — is partitioned by *origin*, and two subdomains are two origins, so each has its own store with no way to read the other's. Cookies are the outlier because they are scoped by host and path and can be deliberately widened to the parent domain.
- Why does the Public Suffix List include entries like github.io that are not TLDs?Because anyone can register a name directly beneath them, so treating them as ordinary domains would put unrelated customers in one site. Listing `github.io` makes `alice.github.io` its own registrable domain, so it cannot set a domain-wide cookie that `bob.github.io` would receive.
- Is every same-origin pair also same-site?Yes — a site is strictly coarser, so identical scheme, host and port implies the same registrable domain and scheme. The interesting direction is the other one: same-site but cross-origin covers every pair of subdomains, and that pair gets no scripting or storage access to each other at all.
saying these in an interview costs you the question
- Says a site is just the domain, ignoring the suffix list
- Assumes subdomains share localStorage because they share cookies
- Treats co.uk as a registrable domain
- Thinks the port distinguishes two sites
- Uses origin and site interchangeably in one answer