CORS: Cross-Origin Resource Sharing Complete Guide
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
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 :443These are different origins:
https://example.comvshttp://example.com(different protocol)https://example.comvshttps://api.example.com(different host)https://example.comvshttps://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 originSimple 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 responseConfiguring 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
| Header | Purpose | Example |
|---|---|---|
Access-Control-Allow-Origin | Which origins are allowed | https://myapp.com or * |
Access-Control-Allow-Methods | Allowed HTTP methods | GET, POST, PUT |
Access-Control-Allow-Headers | Allowed request headers | Content-Type, Authorization |
Access-Control-Expose-Headers | Which headers JS can read | X-Request-ID |
Access-Control-Allow-Credentials | Allow cookies/auth headers | true |
Access-Control-Max-Age | Cache 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
Try It Yourself
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