Skip to content

Access-Control-Allow-Origin Deep Dive — Wildcard, Dynamic, and Multiple Origins

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Access. We cover key concepts, practical examples, and best practices to help you master this topic.

The Access-Control-Allow-Origin header controls which origins can read the response. It accepts a single origin, the wildcard *, or null, with specific behavioral rules for each value.

What You'll Learn

  • When to use wildcard vs specific origins
  • How to implement dynamic origin resolution
  • How to handle multiple origins and the Vary header

Why It Matters

Access-Control-Allow-Origin is the most important CORS header. Getting it wrong blocks legitimate users or opens security holes. Doda Browser's extension API uses dynamic origin validation to allow only registered extension domains.

flowchart TD
    A["Request Origin: https://app.example.com"] --> B{"Server checks whitelist"}
    B -->|"Origin is whitelisted"| C["Set ACAO to the request origin"]
    B -->|"Origin not whitelisted"| D["Return response without ACAO"]
    B -->|"Wildcard mode"| E["Set ACAO to *"]
    C --> F["Include Vary: Origin"]
    E --> G["Cannot use with credentials"]
    style C fill:#86efac,stroke:#16a34a
    style D fill:#fecaca,stroke:#dc2626
    style E fill:#fef08a,stroke:#ca8a04

Code Examples

// Browser sends the Origin header automatically
// JavaScript cannot read Access-Control-Allow-Origin
// But you can infer origin access:
fetch('https://api.example.com/user', {
  credentials: 'include'
}).then(response => {
  // If we got here, the origin was allowed
  console.log('Origin is whitelisted');
});
# Dynamic origin resolution in Flask
ALLOWED_ORIGINS = {
    'https://app.example.com',
    'https://dashboard.example.com',
    'https://admin.example.com'
}

@app.after_request
def set_cors_origin(response):
    origin = request.headers.get('Origin')
    if origin in ALLOWED_ORIGINS:
        response.headers['Access-Control-Allow-Origin'] = origin
        response.headers['Vary'] = 'Origin'
    return response
// Node.js dynamic origin with cors package
const cors = require('cors');

const corsOptions = {
  origin: function (origin, callback) {
    const whitelist = [
      'https://app.example.com',
      'https://dashboard.example.com'
    ];
    // Allow requests with no origin (server-to-server, curl)
    if (!origin || whitelist.indexOf(origin) !== -1) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true
};
# Test Access-Control-Allow-Origin behavior
curl -I -H "Origin: https://app.example.com" \
  https://api.example.com/data | grep "access-control-allow-origin"

# Test with a blocked origin
curl -I -H "Origin: https://evil.com" \
  https://api.example.com/data | grep "access-control-allow-origin"

Common Mistakes

1. Using Wildcard * with Credentials

The browser rejects the response when Allow-Origin is * and Allow-Credentials is true.

2. Returning Multiple Origins in the Header

Only one origin or * is valid. Returning two origins causes the browser to reject the response.

3. Not Returning the Vary Header

Without Vary: Origin, CDNs may cache and serve the wrong origin response to different users.

4. Allowing null Origin Too Broadly

The null origin can be set by sandboxed iframes or data: URLs, making it risky to whitelist.

5. Hardcoding Origins in Frontend Code

Origins should be configured server-side, not hardcoded in client JavaScript.

Practice Questions

  1. What values can Access-Control-Allow-Origin take?
  2. Why can't you use * with credentials?
  3. What is the Vary: Origin header for?
  4. How do you support multiple origins?
  5. What is the null origin and why is it dangerous to allow?

Answers:

  1. A single origin, the wildcard *, or null.
  2. The wildcard indicates any origin is allowed, which contradicts the specificity required for credentialed requests.
  3. It tells caches to vary the cached response based on the Origin request header.
  4. Dynamically set the Access-Control-Allow-Origin header to the requesting origin after validation.
  5. The null origin is sent by sandboxed contexts. Allowing it enables attacks from sandboxed iframes.

Challenge: Implement a dynamic CORS middleware that supports a whitelist of origins, handles credentials, sets the Vary header, and logs rejected origins for security monitoring.

FAQ

Can Access-Control-Allow-Origin be a comma-separated list?

No. The spec does not support multiple origins in a single header. Use dynamic origin resolution based on the request Origin header instead.

What happens if the server does not return Access-Control-Allow-Origin at all?

The browser blocks JavaScript from reading the response. The request may still reach the server but the response is invisible to the client.

Is it safe to echo back the Origin header as Access-Control-Allow-Origin?

Only if you validate the origin against a whitelist. Echoing any origin without validation allows any website to read the response.

Does the wildcard * allow all origins including null?

Yes. The wildcard * allows all origins, including null. This is why it cannot be used with credentials.

How does the browser handle redirects with Access-Control-Allow-Origin?

The browser checks CORS headers on the redirect response as well. If the redirect target is a different origin, it must have its own CORS headers.

Mini Project

Build an origin whitelist service: a middleware that reads from a database or config file, validates incoming origins, sets the appropriate Access-Control-Allow-Origin, and logs rejected origins. Include a test suite that verifies correct behavior for allowed, denied, and missing origins.

What's Next

Explore Access-Control-Allow-Methods to control which HTTP methods are permitted, then study Access-Control-Allow-Headers for custom header handling.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro