Skip to content
CORS: Cross-Origin Resource Sharing Complete Guide

CORS: Cross-Origin Resource Sharing Complete Guide

DodaTech Updated Jun 20, 2026 6 min read

CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls how web pages access resources from a different origin — preventing malicious websites from reading sensitive data while allowing legitimate cross-origin requests.

What You’ll Learn

By the end of this tutorial, you’ll understand the same-origin policy, how CORS works (simple vs preflight requests), how to configure CORS headers on the server, common CORS errors and how to debug them, and proxy solutions for development. Prerequisites: HTTP Protocol basics and Python fundamentals.

Why It Matters

Every time you fetch data from an API on a different domain, make a request from a frontend app to a backend, or load fonts from a CDN — CORS is involved. Misconfiguring it breaks your application.

Real-World Use

Your React app at app.example.com fetches data from api.example.com. Without proper CORS headers, the browser blocks the request. CORS headers tell the browser this cross-origin access is allowed.

CORS Flow


sequenceDiagram
  participant Browser
  participant Frontend as Frontend (origin-A.com)
  participant API as API (api-B.com)
  
  Browser->>Frontend: Load page
  Frontend->>API: OPTIONS (Preflight)
  Note over API: Check Origin header
  API-->>Frontend: Access-Control-Allow-Origin: origin-A.com
  Frontend->>API: GET /data (Actual Request)
  API-->>Frontend: Response + CORS Headers
  Note over Browser: Verify CORS headers
  Browser->>Frontend: Data available to JS

Prerequisites: HTTP Protocol fundamentals, Web Security basics.

What Is Same-Origin Policy?

The same-origin policy is a browser security feature that blocks web pages from accessing resources from a different origin. An origin is defined by:

Protocol + Host + Port
https://   example.com   :443

These are different origins:

  • https://example.com vs http://example.com (different protocol)
  • https://example.com vs https://api.example.com (different host)
  • https://example.com vs https://example.com:8080 (different port)
from urllib.parse import urlparse

def same_origin(url1, url2):
    o1, o2 = urlparse(url1), url2 = urlparse(url2)
    result = (o1.scheme == o2.scheme and
              o1.hostname == o2.hostname and
              o1.port == o2.port)
    print(f"{url1} vs {url2}: {'Same' if result else 'Different'} origin")

same_origin("https://example.com/page1", "https://example.com/page2")
same_origin("https://example.com", "https://api.example.com")
same_origin("http://example.com", "https://example.com")

Expected output:

https://example.com/page1 vs https://example.com/page2: Same origin
https://example.com vs https://api.example.com: Different origin
http://example.com vs https://example.com: Different origin

Simple Requests vs Preflight

CORS distinguishes between simple requests and preflight requests.

Simple requests (no preflight needed) must satisfy ALL conditions:

  • Method: GET, HEAD, or POST
  • Headers: only Accept, Accept-Language, Content-Language, Content-Type (with values: application/x-www-form-urlencoded, multipart/form-data, or text/plain)
  • No event listeners on XMLHttpRequestUpload

Preflight requests use the OPTIONS method to check if the actual request is safe:

# app.py — Python Flask server with CORS
from flask import Flask, jsonify, make_response, request

app = Flask(__name__)

@app.route('/api/data', methods=['GET', 'OPTIONS'])
def get_data():
    if request.method == 'OPTIONS':
        # Preflight response
        response = make_response()
        response.headers['Access-Control-Allow-Origin'] = 'https://myapp.com'
        response.headers['Access-Control-Allow-Methods'] = 'GET, POST'
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
        response.headers['Access-Control-Max-Age'] = '3600'
        return response

    # Actual response
    response = jsonify({"message": "CORS-enabled data"})
    response.headers['Access-Control-Allow-Origin'] = 'https://myapp.com'
    return response

Configuring CORS on the Server

Python (FastAPI)

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myapp.com", "https://admin.myapp.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Content-Type", "Authorization", "X-API-Key"],
    expose_headers=["X-Request-ID", "X-RateLimit-Remaining"],
    max_age=3600,
)

@app.get("/api/data")
def read_data():
    return {"message": "Hello from CORS-enabled API"}

Python (Flask)

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app, origins=["https://myapp.com"], supports_credentials=True)

@app.route('/api/data')
def get_data():
    return {"message": "CORS-enabled with Flask-CORS"}

Node.js (Express)

const express = require('express');
const cors = require('cors');
const app = express();

const corsOptions = {
  origin: ['https://myapp.com', 'https://admin.myapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 3600
};

app.use(cors(corsOptions));

app.get('/api/data', (req, res) => {
  res.json({ message: 'CORS-enabled with Express' });
});

CORS Headers Reference

HeaderPurposeExample
Access-Control-Allow-OriginWhich origins are allowedhttps://myapp.com or *
Access-Control-Allow-MethodsAllowed HTTP methodsGET, POST, PUT
Access-Control-Allow-HeadersAllowed request headersContent-Type, Authorization
Access-Control-Expose-HeadersWhich headers JS can readX-Request-ID
Access-Control-Allow-CredentialsAllow cookies/auth headerstrue
Access-Control-Max-AgeCache preflight result (seconds)3600

CORS with Credentials

When your request includes cookies or authorization headers, you need special handling:

// Frontend — include credentials
fetch('https://api.example.com/data', {
  method: 'GET',
  credentials: 'include',  // Send cookies
  headers: {
    'Authorization': 'Bearer token123'
  }
});
# Server must explicitly allow credentials
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myapp.com"],  # Cannot use "*" with credentials!
    allow_credentials=True,  # Required for cookies/auth
    allow_methods=["*"],
    allow_headers=["*"],
)

Important: When allow_credentials=True, you cannot use Access-Control-Allow-Origin: *. You must specify exact origins.

Common CORS Errors

1. Missing Access-Control-Allow-Origin

The server doesn’t include the CORS header. Add the header on the server or use a proxy.

2. Wildcard with Credentials

Using Access-Control-Allow-Origin: * when sending credentials. Either remove credentials or use specific origins.

3. Preflight Request Failing

The OPTIONS request returns a non-2xx status. Ensure your server handles OPTIONS requests correctly.

4. CORS with Custom Headers

Adding a custom header (e.g., X-API-Key) triggers a preflight. List it in Access-Control-Allow-Headers.

5. Proxy Not Forwarding Headers

Development proxies (like webpack-dev-server) don’t add CORS headers automatically. They just forward requests — the API still needs to respond with CORS headers.

6. Origin Mismatch

https://myapp.com vs https://myapp.com/ (trailing slash) vs https://MyApp.com (case). Origins must match exactly.

Practice Questions

1. What defines a web origin? The combination of protocol (scheme), hostname, and port. Different in any one = different origin.

2. What triggers a preflight (OPTIONS) request? Non-simple methods (PUT, DELETE), custom headers, non-standard Content-Types, or including credentials.

3. Why can’t you use * with credentials? Security: allowing any origin with credentials would let any website make authenticated requests on behalf of the user. You must specify exact origins.

4. How do you debug CORS issues? Check the browser’s Network tab for the preflight (OPTIONS) request, examine response headers, and ensure the origin matches exactly.

5. Challenge: Build a CORS debugging tool Create a simple HTML page that makes fetch requests to various endpoints and displays the CORS headers received. Test with different origins, methods, and headers.

FAQ

Can CORS be bypassed?
CORS is enforced by browsers, not servers. Server-to-server requests have no CORS restrictions. Tools like curl and Postman bypass CORS automatically.
Is it safe to use Access-Control-Allow-Origin: *?
Only for public APIs (like weather data) with no authentication. Never use * for APIs that require cookies, sessions, or auth tokens.
How do I handle CORS in development?
Use a proxy (webpack-dev-server, CORS proxy) or configure CORS on your backend. The http-proxy-middleware package is popular for React apps.
Does CORS apply to mobile apps?
No. CORS is a browser-only mechanism. Native mobile apps and desktop applications don’t enforce CORS — they can make requests to any origin.

Try It Yourself

▶ Try It Yourself Edit the code and click Run

Mini Project: CORS Debugging Proxy

Build a simple CORS proxy in Python that adds CORS headers to any API response. Useful for development when you don’t control the API server. Security angle: Durga Antivirus Pro uses strict CORS policies on its API endpoints to prevent cross-origin data theft — only the official Doda Browser and web interface are allowed origins.

What’s Next

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

What’s Next

Congratulations on completing this CORS tutorial! Here’s where to go from here:

  • Practice daily — Enable CORS on your own APIs using the patterns above
  • Build a project — Create a public API with proper CORS configuration
  • Explore related topics — Check out Cookies and Sessions and HTTP Caching

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro