Cross‑Origin Resource Sharing (CORS) is a critical security feature that browsers enforce to protect users from malicious websites. If you’ve ever built a Python API and struggled with “blocked by CORS policy” errors, you’re not alone. This guide walks you through everything you need to know to configure CORS correctly in popular Python frameworks such as Flask, Django, and FastAPI. By the end, you’ll have a solid, SEO‑friendly understanding of how to enable safe cross‑origin requests, avoid common pitfalls, and keep your applications both functional and secure.
What is CORS and Why It Matters in Python Web Apps
CORS is a set of HTTP headers that tells browsers whether a web page from origin A (e.g., https://example.com) is allowed to request resources from origin B (e.g., https://api.example.com). Without proper CORS configuration, browsers will block these requests, leading to frustrating errors for developers and users alike.
How Browsers Enforce CORS
- Simple requests: Browsers automatically add an
Originheader and expect anAccess-Control-Allow-Originresponse. - Preflight requests: For methods like
PUTor custom headers, browsers send anOPTIONSrequest first to verify permissions. - Credentials: Cookies, HTTP authentication, and client‑side SSL certificates require the
Access-Control-Allow-Credentialsheader.
When your Python server fails to include the correct headers, the browser halts the request, and you see the dreaded “CORS policy” error in the console.
Common Python Frameworks and Their CORS Solutions
Flask with Flask‑CORS
Flask is lightweight, making it a favorite for micro‑services. The Flask‑CORS extension adds the necessary headers with minimal code.
Django with django‑cors‑headers
Django’s “batteries‑included” philosophy means you often need a dedicated middleware like django‑cors‑headers to handle CORS across all apps.
FastAPI with Starlette’s CORS middleware
FastAPI, built on Starlette, includes a built‑in CORS middleware that’s both fast and type‑safe, perfect for modern async APIs.
Step‑by‑Step CORS Configuration for Each Framework
Installing the Required Packages
# Flask
pip install Flask-CORS
# Django
pip install django-cors-headers
# FastAPI
pip install fastapi[all] # includes starlette
Configuring Flask
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
# Basic configuration – allow all origins (not recommended for production)
CORS(app)
# Fine‑grained control
CORS(app, resources={
r"/api/*": {
"origins": ["https://example.com", "https://admin.example.com"],
"methods": ["GET", "POST", "PUT", "DELETE"],
"allow_headers": ["Content-Type", "Authorization"],
"supports_credentials": True
}
})
@app.route("/api/data")
def data():
return {"message": "CORS configured!"}
Key points:
- Use the
resourcesdict to limit CORS to specific routes. - Set
supports_credentials=Trueonly when you truly need cookies or auth headers.
Configuring Django
First, add the middleware and app to settings.py:
# settings.py
INSTALLED_APPS = [
# …
"corsheaders",
# …
]
MIDDLEWARE = [
# CORS must be placed as high as possible
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
# …
]
# Allow specific origins
CORS_ALLOWED_ORIGINS = [
"https://example.com",
"https://app.example.com",
]
# Optional: restrict methods and headers
CORS_ALLOW_METHODS = [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE",
]
CORS_ALLOW_HEADERS = [
"content-type",
"authorization",
"x-requested-with",
]
# Enable credentials if needed
CORS_ALLOW_CREDENTIALS = True
Remember to restart the Django server after updating settings.py.
Configuring FastAPI
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"https://example.com",
"https://dashboard.example.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Authorization", "Content-Type"],
)
@app.get("/items/")
async def read_items():
return {"message": "CORS is active!"}
FastAPI’s middleware approach makes it easy to toggle CORS on or off per deployment environment.
Advanced CORS Settings and Best Practices
Limiting Origins and Methods
Never use a wildcard (*) in production. Instead, maintain a whitelist of trusted domains. This reduces the attack surface and prevents accidental data leakage.
- Origins: Use exact scheme, host, and port (e.g.,
https://api.example.com:443). - Methods: Only enable HTTP verbs your API truly supports.
- Headers: Restrict to the minimal set required for your client.
Handling Credentials Securely
If Access-Control-Allow-Credentials is set to true, the Access-Control-Allow-Origin header cannot be a wildcard. Always pair them with a specific origin list. Additionally, consider the following security measures:
- Set
SameSite=LaxorStricton cookies to mitigate CSRF. - Use HTTPS everywhere to protect authentication tokens.
- Implement short‑lived JWTs instead of long‑lived session cookies.
Testing CORS with Browser DevTools and Curl
Before deploying, verify your headers:
- Browser: Open the Network tab, inspect the request, and look for
Access-Control-Allow-*headers in the response. - Curl example:
curl -i -X OPTIONS https://api.example.com/data \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Authorization,Content-Type"
The response should include Access-Control-Allow-Origin, Access-Control-Allow-Methods, and any other headers you configured.
Frequently Asked Questions
Can I disable CORS completely?
Disabling CORS (Access-Control-Allow-Origin: * with no restrictions) is only safe for public APIs that do not rely on cookies or authentication. For private or user‑specific data, always enforce a strict whitelist.
How to allow subdomains?
Most libraries accept regular expressions or dynamic checks. For Flask‑CORS:
CORS(app, resources={r"/api/*": {"origins": r"https?://.*\.example\.com"}})
In Django, you can compute CORS_ALLOWED_ORIGINS at runtime based on ALLOWED_HOSTS.
What about WebSockets?
WebSockets use the Upgrade header, not standard CORS. However, browsers still enforce the same origin policy. In FastAPI, you can set allow_origins in the WebSocket route decorator, and in Django Channels you can configure allowed_hosts similarly.
Conclusion
Proper CORS configuration is a cornerstone of modern Python web development. By selecting the right library for your framework—Flask‑CORS, django‑cors‑headers, or FastAPI’s built‑in middleware—you can tailor cross‑origin permissions to your exact security requirements. Remember to whitelist trusted origins
Leave a Reply