The same-origin policy is the fundamental browser security model that restricts how a document or script loaded from one origin can read data from or interact with resources of another origin. An origin is the combination of scheme, host and port, and two URLs share an origin only when all three match exactly.
Among browser technologies, the same-origin policy is the reason a malicious page cannot silently read a user’s webmail or bank account in another tab, even though the browser holds valid session cookies for those sites. It does not block every cross-origin request; it mainly blocks reading the responses, while controlled exceptions such as CORS let servers opt in to sharing their data.
How browsers define an origin
An origin is compared as a tuple of scheme, host and port. The path, query string and fragment play no role. If any part of the tuple differs, the browser treats the two URLs as different origins.
The table below compares several addresses with the origin formed by HTTPS, the host shop.example.com and the default HTTPS port 443.
| Compared address | Same origin | Reason |
|---|---|---|
| shop.example.com/cart over HTTPS | Yes | Only the path differs |
| shop.example.com/help over HTTPS on port 443 | Yes | 443 is the default HTTPS port |
| shop.example.com over plain HTTP | No | Different scheme |
| api.example.com over HTTPS | No | Different host, even within the same site |
| shop.example.com over HTTPS on port 8443 | No | Different port |
Browsers also use the broader concept of a site, defined as the scheme plus the registrable domain. The subdomains shop.example.com and api.example.com are same-site but cross-origin. This distinction matters because cookie rules such as SameSite work at the site level, while the same-origin policy works at the stricter origin level.
What the policy allows and what it blocks
The policy is often misunderstood as a ban on all cross-origin communication. In reality, the web depends on embedding resources across origins, and the policy primarily protects the confidentiality of responses.
| Interaction | Default behavior | Example |
|---|---|---|
| Embedding resources | Generally allowed | Images, scripts, stylesheets, video and iframes from other origins |
| Sending navigations and form posts | Allowed | A link or form targeting another site |
| Reading fetch or XHR responses | Blocked unless CORS permits | Script reading JSON from another API |
| Accessing another frame’s DOM | Blocked | Script reading content of a cross-origin iframe |
| Reading storage | Isolated per origin | localStorage, IndexedDB and cache storage |
| Reading canvas pixels from foreign images | Blocked after the canvas is tainted | getImageData on a cross-origin image |
Because cross-origin requests can still be sent, the same-origin policy alone does not prevent cross-site request forgery. A hostile page can submit a form to another site with the user’s cookies attached. Defenses such as SameSite cookies and anti-forgery tokens address that separate threat.
Controlled exceptions and relaxations
Legitimate applications often need cross-origin access, for example a web front end on one origin calling an API on another. Browsers provide explicit, opt-in mechanisms for these cases.
| Mechanism | How it works | Typical use |
|---|---|---|
| CORS | Server returns Access-Control-Allow-Origin and related headers | APIs consumed by front ends on other origins |
| postMessage | Windows exchange messages and verify the sender origin | Communication with embedded iframes or popups |
| Preflight request | Browser sends OPTIONS before non-simple requests | Requests with custom headers or methods such as PUT |
| Credentialed CORS | Allow-Credentials true with a specific origin, never a wildcard | Cookie-authenticated cross-origin APIs |
| Reverse proxy | Requests routed through the same origin | Avoiding cross-origin calls altogether |
CORS relaxes the policy only for reading, and the server, not the client, decides. A response with Access-Control-Allow-Origin: * is readable by any origin but cannot be combined with credentials. Changing the client code cannot bypass a missing CORS header, which is why CORS errors must be fixed on the server.
The older technique of setting document.domain to a shared parent domain is deprecated and being removed from browsers, because it weakened origin isolation. The JSONP pattern, which loaded data as executable scripts, has likewise been replaced by CORS for security reasons.
Related isolation mechanisms
Modern browsers reinforce the same-origin policy with additional isolation layers. These were strengthened after side-channel attacks such as Spectre showed that data in the same process could leak across origins.
- Site isolation places documents from different sites in separate operating system processes, inside the browser sandbox.
- Cross-Origin Resource Policy lets a server state that a resource may only be loaded by the same site or origin.
- Cross-Origin Opener Policy and Embedder Policy together enable cross-origin isolation, which is required for powerful features such as SharedArrayBuffer.
- Content Security Policy restricts which origins a page may load scripts and other resources from, limiting the impact of injected code.
These mechanisms complement each other. The same-origin policy defines what scripts may read, while process isolation ensures that a compromised renderer cannot simply read another origin’s memory.
Common misconceptions and mistakes
Developers usually meet the same-origin policy as an error message, which leads to misguided workarounds. The most frequent misunderstandings are listed below.
- Treating CORS errors as client bugs: the fix is a correct header on the server or a same-origin proxy.
- Reflecting any Origin header with credentials allowed, which effectively disables the protection for authenticated data.
- Assuming subdomains are trusted: api.example.com and shop.example.com are different origins.
- Using postMessage without checking event.origin, which lets any page send commands to the receiving window.
- Believing the policy stops CSRF, when it only protects reading responses.
In plain words
The same-origin policy is a rule that keeps websites out of each other’s business. A page from one website can show pictures from another site. It cannot quietly read private information from that site.
Imagine having your email open in one tab and a strange website in another. That website cannot read your inbox, even though your browser is logged in. Each website stays inside its own box unless the other site openly allows sharing.
A real-world example
A company hosts its single-page application on app.example.com and its API on api.example.com, both over HTTPS. After launch, the browser console shows CORS errors for every request, because the two hosts are different origins and the API returns no CORS headers. A developer first suggests a browser extension that disables the check, which would only hide the problem on one machine.
The backend team instead configures the API to return an Access-Control-Allow-Origin header naming the application origin exactly, together with Access-Control-Allow-Credentials: true, and to answer preflight requests for PUT and DELETE. Requests from the application now succeed, while scripts on any other origin still cannot read the API responses, so authenticated customer data remains protected.