Skip to content

CORS in Flask — Configuring flask-cors and Custom Middleware for Python APIs

DodaTech Updated 2026-06-28 3 min read

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

  1. What extension provides CORS support in Flask?
  2. How do you configure CORS globally in Flask?
  3. What decorator enables CORS on a single route?
  4. How do you allow credentials in flask-cors?
  5. Can you use regex patterns for origins in flask-cors?

Answers:

  1. flask-cors.
  2. Call CORS(app, origins=[...]) after creating the Flask app.
  3. @cross_origin().
  4. Set supports_credentials=True.
  5. 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

Does flask-cors handle OPTIONS preflight automatically?

Yes. flask-cors intercepts OPTIONS requests and returns CORS headers based on configuration. It handles preflight transparently.

Can I use flask-cors with Flask blueprints?

Yes. Apply CORS to the app globally, or use @cross_origin decorators on blueprint routes. flask-cors works with Flask's blueprint system.

How do I debug CORS in Flask?

Set Flask's logging to DEBUG: app.logger.setLevel(logging.DEBUG). flask-cors will log CORS-related decisions and header values.

Does flask-cors work with Flask-RESTful?

Yes. flask-cors works at the Flask application level, so it applies to all extensions including Flask-RESTful.

What is the performance impact of flask-cors?

Minimal. flask-cors adds headers to responses and handles OPTIONS requests. The overhead is microseconds per request.

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