Same-Origin Policy
The Same-Origin Policy (SOP) is a fundamental security mechanism that restricts how documents/scripts from one origin can interact with resources from another origin.
What is an Origin?
An origin is defined by the combination of:
- Protocol (http/https)
- Hostname (domain.com)
- Port (80, 443, etc.)
https://www.example.com:443/page
|____| |______________||__| |___|
protocol hostname port path
Same Origin:
https://www.example.com/page ✅
https://www.example.com/other ✅
Different Origins:
http://www.example.com/page ❌ (different protocol)
https://api.example.com/page ❌ (different hostname)
https://www.example.com:8080 ❌ (different port)
What Same-Origin Policy Allows
The Same-Origin Policy has specific rules about what's allowed and what's blocked.
What's Allowed (Same Origin)
// Same origin - everything works
const api = 'https://www.example.com';
// Read DOM
document.querySelector('#content');
// Make requests
fetch(`${api}/data`);
// Access cookies
document.cookie;
// Access localStorage
localStorage.getItem('key');
What's Blocked (Cross Origin)
// Different origin - restricted
const external = 'https://api.other.com';
// ❌ Read DOM of iframe
const iframe = document.querySelector('iframe');
iframe.contentDocument.body; // Blocked!
// ❌ Read response body (without CORS)
fetch(external).then(res => res.text()).then(data => {
// Blocked if no CORS headers!
});
// ❌ Access cookies
document.cookie; // Only shows cookies for current origin
// ❌ Access localStorage of other origin
// Each origin has its own storage
What's Allowed Without CORS
| Action | Allowed? |
|---|---|
| Simple GET requests | ✅ (but can't read response) |
| Simple POST (form data) | ✅ (but can't read response) |
| Reading response body | ❌ |
| Custom headers | ❌ |
| Non-simple methods (PUT, DELETE) | ❌ |
Why SOP Exists
Without SOP:
Attacker's site (evil.com)
└── iframe with your bank (bank.com)
└── JavaScript reads your account balance
└── Sends to attacker's server
With SOP:
Attacker's site (evil.com)
└── iframe with your bank (bank.com)
└── JavaScript blocked from reading iframe content
└── Attack prevented!
CORS Headers
CORS (Cross-Origin Resource Sharing) is a mechanism that allows servers to explicitly permit cross-origin requests.
How CORS Works
1. Browser sends request to different origin
GET https://api.example.com/data
Origin: https://www.mysite.com
2. Server responds with CORS headers
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.mysite.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
3. Browser allows JavaScript to read the response
Key CORS Headers
Request Headers (sent by browser):
| Header | Purpose |
|---|---|
Origin |
The origin making the request |
Access-Control-Request-Method |
Method for preflight |
Access-Control-Request-Headers |
Headers for preflight |
Response Headers (sent by server):
| Header | Purpose | Example |
|---|---|---|
Access-Control-Allow-Origin |
Allowed origins | * or https://example.com |
Access-Control-Allow-Methods |
Allowed methods | GET, POST, PUT, DELETE |
Access-Control-Allow-Headers |
Allowed headers | Content-Type, Authorization |
Access-Control-Allow-Credentials |
Allow cookies | true |
Access-Control-Max-Age |
Preflight cache time | 86400 (24 hours) |
Access-Control-Expose-Headers |
Headers client can read | X-Request-ID |
Simple vs Non-Simple Requests
Simple Request (no preflight):
- Method: GET, POST, HEAD
- Headers: Only simple headers (Accept, Content-Type, etc.)
- Content-Type: text/plain, multipart/form-data, application/x-www-form-urlencoded
Non-Simple Request (requires preflight):
- Method: PUT, PATCH, DELETE
- Headers: Authorization, Content-Type: application/json
- Custom headers
Server Configuration Examples
Express.js:
const cors = require('cors');
// Allow specific origin
app.use(cors({
origin: 'https://www.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
// Allow multiple origins
app.use(cors({
origin: (origin, callback) => {
const allowed = ['https://www.example.com', 'https://admin.example.com'];
if (!origin || allowed.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));
Nginx:
location /api/ {
add_header Access-Control-Allow-Origin "https://www.example.com";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
}
Preflight Requests
Preflight requests are OPTIONS requests the browser sends to check if the actual request is allowed.
When Preflight Occurs
Non-simple request detected?
├── Yes → Send preflight OPTIONS request
│ ├── Server allows → Send actual request
│ └── Server blocks → Error
└── No → Send request directly
Preflight Flow
Browser Server
| |
| 1. OPTIONS /api/data |
| Origin: https://www.example.com |
| Access-Control-Request-Method: PUT |
| Access-Control-Request-Headers: |
| Content-Type, Authorization |
|--------------------------------------->|
| |
| 2. Preflight Response |
| Access-Control-Allow-Origin: * |
| Access-Control-Allow-Methods: GET, |
| POST, PUT |
| Access-Control-Allow-Headers: |
| Content-Type, Authorization |
| Access-Control-Max-Age: 86400 |
|<---------------------------------------|
| |
| 3. Actual PUT Request |
| Origin: https://www.example.com |
| Content-Type: application/json |
| Authorization: Bearer token |
|--------------------------------------->|
| |
| 4. Actual Response |
| Access-Control-Allow-Origin: * |
| Content-Type: application/json |
|<---------------------------------------|
Caching Preflight Results
// Server can cache preflight for 24 hours
app.options('/api/data', cors({
maxAge: 86400 // 24 hours in seconds
}));
// Browser caches preflight per URL
// Subsequent requests within cache time skip preflight
Handling Preflight in Code
// Express.js with cors middleware
const cors = require('cors');
app.use(cors());
// Manual handling
app.options('/api/data', (req, res) => {
res.header('Access-Control-Allow-Origin', 'https://www.example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Max-Age', '86400');
res.sendStatus(204);
});
// Client-side fetch with CORS
fetch('https://api.example.com/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ name: 'Alice' }),
credentials: 'include' // Send cookies
});
Common CORS Errors
| Error | Cause | Solution |
|---|---|---|
No 'Access-Control-Allow-Origin' |
Server doesn't send CORS header | Configure server CORS |
Method not allowed |
Method not in Allow-Methods | Add method to allowed list |
Header not allowed |
Header not in Allow-Headers | Add header to allowed list |
Credentials not supported |
Allow-Credentials: true with Allow-Origin: * | Use specific origin with credentials |
Practice Problems
Create a reusable React component implementing CORS. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for CORS using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize CORS for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What does the Same-Origin Policy prevent?
2. What is a preflight request?
3. Which request does NOT require preflight?
4. Can you use Access-Control-Allow-Origin: * with credentials?
Flashcards
Question
What is the Same-Origin Policy?
Click to reveal answer
Answer
A security mechanism that restricts JavaScript from reading data from a different origin (protocol + domain + port).
Question
What is CORS?
Click to reveal answer
Answer
Cross-Origin Resource Sharing - a mechanism that allows servers to explicitly permit cross-origin requests via headers.
Question
When does a preflight request occur?
Click to reveal answer
Answer
For non-simple requests: PUT/DELETE methods, custom headers, or Content-Type: application/json.
Question
What does Access-Control-Allow-Origin do?
Click to reveal answer
Answer
Specifies which origins are allowed to access the resource. Can be a specific origin or * for all.
Question
What is CORS?
Click to reveal answer
Answer
CORS is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Same-Origin Policy is a fundamental browser security mechanism
- 2.CORS headers allow servers to explicitly permit cross-origin requests
- 3.Preflight requests check permissions for non-simple requests
- 4.Simple GET/POST requests don't require preflight
- 5.Use specific origins instead of * when using credentials
Interview Tips
- •Explain the Same-Origin Policy and why it exists
- •Know the difference between simple and non-simple requests
- •Understand the preflight flow and when it occurs
- •Be able to configure CORS in a backend framework
Cheat Sheet
CORS Cheat Sheet
Same-Origin Policy:
- Restricts JavaScript from cross-origin reads
- Origin = protocol + domain + port
- Allows cross-origin writes (forms, scripts)
- Blocks cross-origin reads without CORS
CORS Headers:
- Access-Control-Allow-Origin: Allowed origins
- Access-Control-Allow-Methods: Allowed HTTP methods
- Access-Control-Allow-Headers: Allowed request headers
- Access-Control-Allow-Credentials: Allow cookies
- Access-Control-Max-Age: Preflight cache time
Simple vs Non-Simple:
- Simple: GET/POST/HEAD with basic headers
- Non-Simple: PUT/DELETE, custom headers, JSON body
- Non-simple requires preflight
Preflight:
- OPTIONS request to check permissions
- Cached per URL (max-age)
- Skipped for simple requests