Skip to content
beginnerPhase 29 · Web Foundations

CORS

Master Cross-Origin Resource Sharing: preflight requests, headers, and security implications.

45m
0 problems
Topic Progress0%

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

0/3solved
Build CORS Component

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 needed
CORS Testing

Write 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 tests
CORS Performance

Optimize 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 analysis

Quiz

1. What does the Same-Origin Policy prevent?

Question 1 options

2. What is a preflight request?

Question 2 options

3. Which request does NOT require preflight?

Question 3 options

4. Can you use Access-Control-Allow-Origin: * with credentials?

Question 4 options

Flashcards

Question

What is the Same-Origin Policy?

Answer

A security mechanism that restricts JavaScript from reading data from a different origin (protocol + domain + port).

Question

What is CORS?

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?

Answer

For non-simple requests: PUT/DELETE methods, custom headers, or Content-Type: application/json.

Question

What does Access-Control-Allow-Origin do?

Answer

Specifies which origins are allowed to access the resource. Can be a specific origin or * for all.

Question

What is CORS?

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