CORS in Flask — Configuring flask-cors and Custom Middleware for Python APIs
In this tutorial, you will learn about CORS in Flask. We cover key concepts, practical examples, and best practices to help you master this topic.
Flask offers CORS configuration through the flask-cors extension, providing decorators for per-route control, CORS() for global configuration, and custom middleware for advanced scenarios.
What You'll Learn
- Installing and configuring flask-cors
- Using @cross_origin decorator per route
- Custom CORS middleware for complex logic
Why It Matters
Flask powers many lightweight APIs and Microservices. The flask-cors extension makes CORS configuration simple while supporting advanced use cases. DodaTech uses Flask for its internal tooling APIs with flask-cors for browser-based admin tools.
flowchart LR
A["Flask API"] --> B{"CORS Approach"}
B -->|"Simple global"| C["CORS(app)"]
B -->|"Per route"| D["@cross_origin() decorator"]
B -->|"Custom"| E["after_request middleware"]
C --> F["All routes get CORS"]
D --> G["Specific routes get CORS"]
E --> H["Full control"]
Code Examples
# Global CORS configuration with flask-cors
from flask import Flask, jsonify
from flask_cors import CORS
app = Flask(__name__)
# Allow all origins (development)
CORS(app)
# With specific configuration
CORS(app, origins=[
'https://app.example.com',
'https://admin.example.com'
], methods=['GET', 'POST', 'PUT', 'DELETE'],
allow_headers=['Content-Type', 'Authorization'],
supports_credentials=True,
max_age=3600)
# Per-route CORS with decorator
from flask import Flask, jsonify
from flask_cors import cross_origin
app = Flask(__name__)
@app.route('/api/public')
@cross_origin() # Default allows all origins
def public_endpoint():
return jsonify({"data": "public"})
@app.route('/api/secure')
@cross_origin(
origins=['https://app.example.com'],
methods=['GET', 'POST'],
headers=['Content-Type', 'Authorization'],
supports_credentials=True
)
def secure_endpoint():
return jsonify({"data": "secure"})
@app.route('/api/admin')
@cross_origin(
origins=['https://admin.example.com'],
methods=['DELETE'],
supports_credentials=True
)
def admin_endpoint():
return jsonify({"deleted": True})
# Custom CORS middleware for dynamic origins
import re
from flask import request
ALLOWED_ORIGIN_PATTERNS = [
re.compile(r'^https://[a-z]+\.example\.com$'),
re.compile(r'^https://app\.example\.com$'),
]
@app.after_request
def dynamic_cors(response):
origin = request.headers.get('Origin')
if origin:
for pattern in ALLOWED_ORIGIN_PATTERNS:
if pattern.match(origin):
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Vary'] = 'Origin'
break
if request.method == 'OPTIONS':
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
# Test Flask CORS
curl -I -H "Origin: https://app.example.com" \
http://localhost:5000/api/secure | grep -i "access-control"
Common Mistakes
1. Configuring CORS After Route Registration
Call CORS(app) before registering routes, or use the @cross_origin decorator on each route.
2. Using supports_credentials=True with Origins=["*"]
The wildcard and credentials conflict applies in flask-cors as well.
3. Forgetting to Handle OPTIONS for Custom Middleware
When using custom middleware, handle OPTIONS requests explicitly.
4. Not Specifying Methods in @cross_origin
Without methods, only GET is allowed. Explicitly list all needed methods.
5. Mixing Global and Per-Route CORS Conflictingly
Per-route decorators override global config for that route. Ensure consistency.
Practice Questions
- What extension provides CORS support in Flask?
- How do you configure CORS globally in Flask?
- What decorator enables CORS on a single route?
- How do you allow credentials in flask-cors?
- Can you use regex patterns for origins in flask-cors?
Answers:
- flask-cors.
- Call CORS(app, origins=[...]) after creating the Flask app.
- @cross_origin().
- Set supports_credentials=True.
- No, flask-cors requires exact origin strings. Use custom middleware for regex patterns.
Challenge: Build a Flask API with three endpoints: public (all origins), authenticated (specific origin with credentials), and admin (pattern-matched subdomains). Implement CORS with both flask-cors and custom middleware where needed.
FAQ
Mini Project
Build a Flask API with a React frontend. Implement CORS using flask-cors for global configuration, add per-route overrides for admin endpoints, and build a debugging dashboard that shows live CORS header information for each request.
What's Next
Learn about NGINX CORS configuration for reverse proxy setups, then explore Apache CORS configuration.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro