How CORS Works - Understanding Cross-Origin Requests

13 min read | 2025.12.10

What is CORS

CORS (Cross-Origin Resource Sharing) is a browser mechanism for safely sharing resources across different origins. For security reasons, browsers restrict requests to different origins by default.

What is an Origin: A combination of scheme (http/https), host, and port. https://example.com:443 and https://api.example.com:443 are different origins.

Same-Origin Policy

Browsers restrict requests to different origins through the Same-Origin Policy (SOP).

From https://app.example.com:

URLResultReason
https://app.example.com/api✓ AllowedSame origin
https://api.example.com/v1✗ BlockedDifferent host
http://app.example.com/api✗ BlockedDifferent scheme
https://app.example.com:8080✗ BlockedDifferent port

Basic CORS Flow

Simple Requests

Requests meeting the following conditions are sent without preflight.

  • Methods: GET, HEAD, POST
  • Headers: Accept, Accept-Language, Content-Language, Content-Type (limited)
  • Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain
sequenceDiagram
    participant Browser
    participant Server
    Browser->>Server: GET /api/users<br/>Origin: https://app.example.com
    Server->>Browser: HTTP/1.1 200 OK<br/>Access-Control-Allow-Origin: https://app.example.com

Preflight Requests

Requests with custom headers or JSON require pre-verification.

sequenceDiagram
    participant Browser
    participant Server

    Note over Browser, Server: 1. Preflight (Pre-verification)
    Browser->>Server: OPTIONS /api/users<br/>Origin: https://app.example.com<br/>Access-Control-Request-Method: POST<br/>Access-Control-Request-Headers: Content-Type, Authorization
    Server->>Browser: HTTP/1.1 204 No Content<br/>Access-Control-Allow-Origin: https://app.example.com<br/>Access-Control-Allow-Methods: POST, GET, OPTIONS<br/>Access-Control-Allow-Headers: Content-Type, Authorization<br/>Access-Control-Max-Age: 86400

    Note over Browser, Server: 2. Actual Request
    Browser->>Server: POST /api/users<br/>Origin: https://app.example.com<br/>Content-Type: application/json<br/>Authorization: Bearer token123

CORS Headers

Response Headers

HeaderDescription
Access-Control-Allow-OriginAllowed origin
Access-Control-Allow-MethodsAllowed HTTP methods
Access-Control-Allow-HeadersAllowed request headers
Access-Control-Allow-CredentialsAllow cookie transmission
Access-Control-Expose-HeadersHeaders accessible from JS
Access-Control-Max-AgePreflight result cache time

Configuration Example (Express.js)

const cors = require('cors');

// Allow specific origin
app.use(cors({
  origin: 'https://app.example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400
}));

// Allow multiple origins
app.use(cors({
  origin: ['https://app.example.com', 'https://admin.example.com'],
}));

// Dynamically validate origin
app.use(cors({
  origin: (origin, callback) => {
    const allowedOrigins = ['https://app.example.com'];
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  }
}));

Credentials Mode

Requests with cookies or authentication require special configuration.

// Client side
fetch('https://api.example.com/data', {
  credentials: 'include'  // Include cookies
});

// Server side
// Access-Control-Allow-Origin: * cannot be used
// Must specify concrete origin
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

Common Errors and Solutions

ErrorCauseSolution
No ‘Access-Control-Allow-Origin’ headerServer not returning CORS headersConfigure CORS on server side, or route requests through a proxy
Wildcard with credentialsCombination of credentials: 'include' and Access-Control-Allow-Origin: *Specify concrete origin: Access-Control-Allow-Origin: https://app.example.com
Method not allowedMethod not allowed in preflightAdd required methods to Access-Control-Allow-Methods
Header not allowedCustom header not allowedAdd header to Access-Control-Allow-Headers

Development CORS Solutions

Using a Proxy

// Vite configuration example
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
});

Browser Extensions

Extensions that disable CORS are available for development only. Do not use in production.

Security Considerations

Wildcard Dangers

ConfigurationRisk LevelDescription
Access-Control-Allow-Origin: *✗ DangerousAllow all origins
Access-Control-Allow-Origin: https://app.example.com✓ RecommendedExplicitly specify allowed origins

Origin Validation

// ✗ Bad example: Return request Origin as-is
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);

// ✓ Good example: Validate against whitelist
const allowedOrigins = ['https://app.example.com'];
if (allowedOrigins.includes(req.headers.origin)) {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
}

Summary

CORS is an important mechanism that enables resource sharing across different origins while maintaining web application security. By properly configuring headers and validating origins, you can achieve secure cross-origin communication.

← Back to list