Skip to content

CORS Configuration for APIs — Complete Cross-Origin Guide

DodaTech Updated 2026-06-28 3 min read

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

Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls which websites can access your API. It prevents malicious sites from reading sensitive data from your API through the user's browser.

What You'll Learn

You'll learn how CORS works, how to configure it correctly, and common misconfigurations that lead to vulnerabilities.

Why It Matters

Misconfigured CORS is a top API security risk. Overly permissive CORS allows any website to read user data. Too restrictive CORS blocks legitimate mobile apps and SPAs.

Real-World Use

A social media API restricts CORS to its own domain and trusted mobile app origins. When a malicious site tries to read user profiles via AJAX, the browser blocks the request due to CORS policy.

flowchart TD
    A[Browser Detects Cross-Origin Request] --> B{Simple Request?}
    B -->|Yes| C[Send Request with Origin Header]
    B -->|No| D[Send Preflight OPTIONS Request]
    D --> E[Server Returns CORS Headers]
    E --> F{Headers Allow Origin?}
    C --> G[Server Returns CORS Headers]
    G --> H{Headers Allow Origin?}
    F -->|Yes| I[Allow Request]
    F -->|No| J[Block by CORS]
    H -->|Yes| I
    H -->|No| J

Teacher's Mindset

CORS is like a nightclub that requires ID. The bouncer (browser) checks your ID (origin) at the door. Even if you have a valid ticket (API key), you need the right ID to enter.

Configuring CORS

from flask import Flask, jsonify, request
from flask_cors import CORS

app = Flask(__name__)

CORS(
    app,
    resources={
        r"/api/*": {
            "origins": ["https://app.example.com", "https://admin.example.com"],
            "methods": ["GET", "POST", "PUT", "DELETE"],
            "allow_headers": ["Content-Type", "Authorization", "X-CSRF-Token"],
            "expose_headers": ["X-RateLimit-Remaining"],
            "supports_credentials": True,
            "max_age": 3600
        }
    }
)

@app.route("/api/data")
def get_data():
    return jsonify({"data": "sensitive"})
# Manual CORS header handling
@app.after_request
def add_cors_headers(response):
    allowed_origins = [
        "https://app.example.com",
        "https://staging.example.com"
    ]
    origin = request.headers.get("Origin")
    if origin in allowed_origins:
        response.headers["Access-Control-Allow-Origin"] = origin
        response.headers["Access-Control-Allow-Credentials"] = "true"
        response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE"
        response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
        response.headers["Access-Control-Max-Age"] = "3600"
    return response

@app.route("/api/data", methods=["OPTIONS"])
def handle_preflight():
    return jsonify({"message": "OK"})
# Dynamic origin validation
VALID_DOMAINS = {"example.com", "app.example.com", "api.example.com"}

def is_valid_origin(origin):
    from urllib.parse import urlparse
    if not origin:
        return False
    parsed = urlparse(origin)
    domain = parsed.netloc or parsed.path
    return any(domain == d or domain.endswith("." + d) for d in VALID_DOMAINS)

@app.after_request
def dynamic_cors(response):
    origin = request.headers.get("Origin")
    if origin and is_valid_origin(origin):
        response.headers["Access-Control-Allow-Origin"] = origin
    return response

Common Mistakes

Mistake Why It's Wrong Fix
Using Access-Control-Allow-Origin: * with credentials Browser rejects the combination Explicitly list allowed origins when using credentials
Reflecting origin without validation Any site can access your API Validate origin against a whitelist
Allowing all headers Increases attack surface Only allow headers your API actually uses
Exposing too many headers Information leakage Only expose headers clients need
No preflight cache Increases request latency Set Access-Control-Max-Age to reduce preflight calls

Practice Questions

  1. What is a CORS preflight request?
  2. Why can't you use wildcard origin with credentials?
  3. What is the difference between simple and preflight requests?
  4. How do you handle CORS for multiple subdomains?
  5. What happens if CORS is too restrictive?

Challenge

Create a Flask API with domain-specific CORS configuration. Test with curl using different Origin headers. Verify that only whitelisted origins receive the CORS headers.

FAQ

Does CORS protect my API from abuse?

No. CORS is a browser-only protection. Server-to-server requests, mobile apps, and curl bypass CORS entirely. Use authentication and rate limiting.

What is the difference between CORS and CSRF?

CORS controls cross-origin reads. CSRF controls cross-origin writes. CORS is enforced by the browser. CSRF requires server-side validation.

Can I use CORS to allow all origins in development?

Yes for local development, but never in production. Use environment-specific CORS configuration.

What is a null origin?

The origin 'null' is sent for file:// URLs and sandboxed iframes. It cannot be whitelisted with credentials. Consider using a proxy for such cases.

How do I debug CORS issues?

Check browser console for CORS errors. Use curl with -H 'Origin: https://example.com' to test server response. Verify the Access-Control-Allow-Origin header.

Mini Project

Build an API that serves different CORS policies for public and authenticated endpoints. Public endpoints allow any origin with Rate Limiting. Authenticated endpoints require specific origins and credentials.

What's Next

Learn about security headers including HSTS, CSP, and X-Frame-Options.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro