How does the ‘Access-Control-Allow-Origin’ header work?

The Access-Control-Allow-Origin (ACAO) header is a critical part of the Cross-Origin Resource Sharing (CORS) mechanism. It allows servers to specify which origins (domains) are permitted to access their resources via web browsers. Here’s a breakdown of how it works:

1. Purpose

  • Security: Enforces the browser’s same-origin policy, preventing unauthorized cross-origin requests.
  • Control: Lets servers explicitly whitelist trusted domains for cross-origin access (e.g., APIs accessed by a frontend app).

2. How It Works

Step 1: Browser Sends a Cross-Origin Request

When a web app at https://example.com tries to fetch data from https://api.otherdomain.com, the browser adds an Origin header to the request:

Origin: https://example.com

Step 2: Server Responds with Access-Control-Allow-Origin

The server at api.otherdomain.com responds with:

Access-Control-Allow-Origin: https://example.com
  • Allowed: If the value matches the Origin header, the browser permits the response to be read.
  • Denied: If there’s a mismatch, the browser blocks the response and throws a CORS error.

3. Common Configurations

Allow a Specific Domain

Access-Control-Allow-Origin: https://example.com

Allow Any Domain (Use Sparingly)

Access-Control-Allow-Origin: *
  • Limitation: Wildcard (*) cannot be used with credentials (e.g., cookies, auth headers). For authenticated requests, specify exact domains.

4. Handling Credentials (Cookies, Auth Headers)

If the client sends credentials (e.g., fetch(..., { credentials: 'include' })):

  1. The server must set:
   Access-Control-Allow-Origin: https://example.com  // Exact domain, NOT wildcard (*)
   Access-Control-Allow-Credentials: true
  1. The client must explicitly include credentials.

5. Preflight Requests

For “complex” requests (e.g., POST with Content-Type: application/json), the browser first sends an OPTIONS request (preflight) to check permissions. The server must respond with:

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization

6. Example Flow

  1. Client Request (from https://example.com):
   GET /data HTTP/1.1
   Origin: https://example.com
  1. Server Response:
   HTTP/1.1 200 OK
   Access-Control-Allow-Origin: https://example.com
   Access-Control-Allow-Methods: GET, POST
  1. Result: The browser allows the response data to be read by https://example.com.

7. Common Errors

  • Missing ACAO Header: Browser blocks the response.
  • Wildcard with Credentials: Access-Control-Allow-Origin: * + Access-Control-Allow-Credentials: true → error.
  • Mismatched Origin: Access-Control-Allow-Origin: https://wrongdomain.com → request blocked.

8. Server-Side Setup Examples

Express.js

const cors = require('cors');
app.use(cors({
  origin: 'https://example.com', // or an array of allowed origins
  credentials: true
}));

Nginx

location / {
  add_header 'Access-Control-Allow-Origin' 'https://example.com';
  add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
  add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
}

Apache (.htaccess)

Header set Access-Control-Allow-Origin "https://example.com"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"

Key Takeaways

  • Security First: Restrict ACAO to specific domains instead of using *.
  • Credentials: Use explicit domains and Access-Control-Allow-Credentials: true.
  • Preflight: Handle OPTIONS requests for non-simple requests (e.g., custom headers).

The Access-Control-Allow-Origin header is enforced by browsers, not servers. Tools like curl or Postman ignore CORS, but browsers strictly enforce it to protect users.

Leave a Reply

Your email address will not be published. Required fields are marked *