Problem?
Browser automatically carry your identity with requests:
- cookies
- login sessions
- saved credentials
- authenticaion headers
Without restrictions, any website you visited could use your browser as a “trusted agent” to interact with other sites where you were logged in.
Example:
- You log into your bank in one tab.
- Later, you visit a malicious website.
- That malicious site silently sends requests to the bank using your browser.
- Because your browser automatically includes your bank cookies, the bank thinks you made the request.
Thus the malicious page could also:
- read the bank's responses
- steal account data
- perform actions as you
This became a huge security issue as the web evolved from static documents into interactive applications.
What exactly is an “origin”?
An origin is:
scheme + host + portExamples:
| URL | Origin |
|---|---|
https://example.com | (https, example.com, 443) |
http://example.com | different origin |
https://api.example.com | different origin |
https://example.com:8080 | different origin |
Two pages are considered “same-origin” only if all three match.
The attack that changed everything
A classic example:
Cross-site data theft
Imagine:
- You are logged into:
- banking
- corporate intranet
Then you visit:
evil.comThen this website could do as follows:
const data = fetch("https://bank.com/account");
console.log(data);The malicious site could:
- read your account balance
- extract private messages
- steal CSRF tokens
- impersonate you
The browser itself became the attack vector.
Why cookies made this dangerous
Browsers were designed for convenience:
- automatic login persistence
- automatic cookie sending
- seamless navigation
So the browser would attack authentication automatically:
Cookie: sessionid=abc123Servers trusted these cookies.
Thus any site could abuse that trust.
Same-Origin Policy
SOP introduced isolation boundaries between web applications.
A page from:
https://evil.comcannot freely:
- read DOM data from
bank.com - access JS variables form
bank.com - inspect responses from
bank.com - manipulate sensitive cross-origin resources
This transformed the browser into a safer multi-application platform.
Important nuance: SOP does NOT block all requests
A page can still send many cross-origin requests.
For example:
- loading images
- loading scripts
- submitting forms
But SOP mainly prevents:
- reading sensitive responses
- direct cross-origin DOM access
That distinction exists because the web depended heavily on cross-site resource loading.
Problems SOP introduced
SOP solved security issues, but created developer friction.
Modern apps often need:
- APIs
- CDNs
- third-party services
- microservices
So browser later added controlled exceptions:
- CORS
- postMessage
- JSONP (older workaround)
- Cross-Origin Resource Sharding headers
- sandboxed iframes
SOP fixed security, but it was too restrictive for modern applications.
The web evolved into distributed systems.
Frontend and backend often live on different origins:
Frontend:
https://myapp.com
API:
https://api.myapp.comAccording to SOP:
- these are different origins
- browser blocks frontend from reading API responses.
Example problem:
Frontend:
fetch("https://api.myapp.com/users")Browser error:
Blocked by Same-Origin PolicyEven though both servers belong to the same company.
Why this became painful
Modern apps need cross-origin communication for:
- APIs
- CDNs
- authentication providers
- payment gateways
- microservices
- cloud storage
SOP became too strict for legitimate user cases.
The Solution: CORS
Browsers introduced CORS (Cross-Origin Resource Sharing).
CORS allows servers to explicitly say:
“I trust this other origin.”
How CORS works
Suppose frontend:
https://app.comcalls:
https://api.comThe browser sends:
Origin: https://app.comThe server responds:
Access-Control-All-Origin: https://app.comThe browser checks:
- Did the server approve this origin?
If yes:
- browser allows JavaScript to read the response
If not:
- browser blocks access
Important Idea
CORS is enforces by the browser.
The server only declares permission.
The browser decides whether frontend JS can access the response.
What Actually Happens
In a CORS failure, the browser usually still sends the HTTP request to the server, but it blocks JavaScript from accessing the response.
Suppose your frontend:
fetch("https://api.com/data")runs from:
https://app.comThe browser sends:
GET /data
Origin: https://app.comNow two possibilities:
Case 1: Server allows it
Server responds:
Access-Control-Allow-Origin: https://app.comBrowser says:
“Okay, this origin is trusted.”
Then JavaScript gets the response:
const data = await response.json();works normally.
Case 2: Server does NOT allow it
Server responds without proper CORS headers:
(no Access-Control-Allow-Origin)The browser still receives the response internally.
But the browser says:
“JavaScript from app.com is not allowed to read this.”
So JS gets:
CORS errorand access is blocked.
Extremely important point
CORS is not server-side security.
It is a browser-enforced reading restriction.
The server may have fully processed the request.
This Surprises many developers
People often think:
“CORS blocked my API call.”
Usually not true.
Most of the time:
- request WAS sent
- server MAY have executed it
- browser simply hid the response from JS
Example: dangerous side effect
Imagine:
fetch("https://bank.com/transfer", {
method: "POST",
body: JSON.stringify({
amount: 1000
})
})Even if CORS fails:
- the request may still reach the server
- money transfer could still happen
Browser only blocks:
response.text()That's why:
- CORS is NOT protection against CSRF
- separate CSRF protections are needed
Leave a comment
Your email address will not be published. Required fields are marked *


