mrkeyoor.com_
Sun 20 Sept 17:55 UTC
PyPIWeb Backendupdated 19 Sept 2026

flask-cors review

Flask-CORS 6.0.5 is a Flask response hook and decorator for Cross-Origin Resource Sharing. It answers browser preflights and adds allow-origin, method, header, credential, exposure, cache, and private-network headers at application, Blueprint, resource-pattern, or view scope. The default policy covers every route and origin but does not allow credentials. Version 6.0.5 fixes strict MyPy use with Blueprints after 6.0.4 introduced complete annotations and `py.typed`; our Python 3.12 install confirmed that typed marker and a working import.

Verdict

Flask-CORS 6.0.5 installed in 0.2 seconds and 3 MB in our sandbox, imported successfully, and had no audit findings. Install it for Flask-owned CORS, but replace the all-origin default with exact production origins and let only one infrastructure layer emit the headers.

We installed it

Lab card: what happened when we installed flask-corsScreenshot of flask-cors documentation
Install✓ · 0.2s8 packages on disk · 3 MB
Importimport flask_cors in 0.69s · pure Python · py.typed · requires Python <4.0,>=3.9
Known vulns0(pip-audit)

Answers from our run

Does flask-cors install cleanly?

Yes. In a fresh container with an empty cache, pip install flask-cors finished in 0.2s, leaving 8 packages and 3 MB on disk. pip-audit reported no known vulnerabilities.

What does flask-cors need to run?

Python <4.0,>=3.9, and nothing compiled: it is pure Python. In our run import flask_cors succeeded in 0.69s, and the package ships py.typed for type checkers.

flask-cors or starlette: which should you use?

starlette: Use its CORSMiddleware in Starlette and FastAPI ASGI applications. Flask-CORS 6.0.5 installed in 0.2 seconds and 3 MB in our sandbox, imported successfully, and had no audit findings.

When should you not use flask-cors?

An edge proxy already owns CORS; duplicate allow-origin headers cause browsers to reject the response.

API stability4/5The public surface remains `CORS` plus `cross_origin`, with the same options available for apps, Blueprints, resource maps, and individual views. Version 6 intentionally changed security-sensitive matching: literal paths sort before regex rules, expression specificity affects order, paths are case-sensitive, and plus signs are preserved. Patch 6.0.5 then corrected strict Blueprint annotations, so upgrades deserve policy tests even though ordinary calls stayed familiar.
Docs4/5Official pages enumerate extension and decorator options, precedence among resource settings and app configuration, credentials, varying by origin, preflight caching, automatic OPTIONS handling, and debug logging. The README supplies examples for whole apps, path maps, and views. A security-sensitive default has differed between prose and source in this release line, so `DEFAULT_OPTIONS` and release notes should settle disputed behavior rather than an old copied example.
Maintenance4/5PyPI published 6.0.5 on June 8, 2026, and GitHub shows a push the following day, 932 stars, and 40 open issues and pull requests. Recent work added typed-package metadata, strict annotations, build-permission hardening, and path-matching fixes. Several rapid corrections followed the 6.0 changes, which demonstrates attention but also makes pinning and testing the selected patch sensible.
Ecosystem5/5The supplied registry measurement is 13,120,422 weekly downloads, and GitHub currently reports 932 stars. Flask-CORS fits Flask factories, Blueprints, decorators, error handlers, and generated OPTIONS responses without adding a separate server. Its reach ends at the Flask response: browser credential flags, cookie attributes, CSRF protection, and proxy cache correctness remain application and infrastructure duties.

Use it if

  • A browser SPA calls a Flask API from a different scheme, hostname, or port.
  • Different Flask URL prefixes need separate browser-origin and credential rules.
  • A Blueprint should carry its own CORS policy when registered in several app factories.
  • Flask-generated OPTIONS and error responses must receive the same cross-origin headers as normal responses.
Skip it if

Setup reality

We installed Flask-CORS 6.0.5 in a clean Python 3.12 Bookworm sandbox in 0.2 seconds. The environment ended with 8 packages and 3 MB on disk. The package declares 3 direct dependencies, is pure Python, requires Python 3.9 or newer below 4.0, and includes py.typed. import flask_cors completed in 0.69 seconds, and pip-audit reported 0 known vulnerabilities. The measured package metadata did not provide a license value, though the GitHub repository identifies MIT.

Calling CORS(app) needs no account or file, but it enables all origins, common methods, request headers, and paths. Write production origins with their scheme and port. supports_credentials is false initially and cannot be combined with a wildcard origin. Cookie-based cross-site sessions also need suitable SameSite and Secure attributes, CSRF defense for state changes, and a browser request configured to send credentials. None of those controls are supplied by an allow-origin header.

Configuration resolves from a matched resource rule, constructor arguments, CORS_* app settings, then defaults. Version 6 validates and compiles resource expressions during setup. Literal paths precede regular expressions, and more specific regex rules win before shorter ones. Matching is case-sensitive. Attach the extension to a Blueprint when only that module should be visible cross-origin; 6.0.5 specifically repaired its strict type signature.

Dynamic origin responses should retain Vary: Origin, or a shared cache may replay one tenant's header to another. max_age governs the browser's successful preflight cache, not Flask response caching. When a policy misses, turn on the flask_cors debug logger and reproduce the exact OPTIONS request with Origin and Access-Control-Request headers. Verify that Flask, Nginx, and a CDN are not each writing their own copy.

Patterns

Permit one browser application allow-frontend

from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins=["https://app.example.com"])

Origins include scheme and port, so a localhost development port needs its own entry.

Apply CORS only below the API prefix scope-url-prefix

CORS(app, resources={r"/api/*": {"origins": ["https://app.example.com"]}})

Resource keys match request paths rather than full URLs; narrow patterns reduce surprising precedence.

Give public and account routes different rules split-public-private

CORS(app, resources={
 r"/api/public/*": {"origins": "*"},
 r"/api/account/*": {"origins": ["https://app.example.com"], "supports_credentials": True}
})

An explicit origin is mandatory for credentialed routes, and cookie-authorized writes still require CSRF protection.

Limit the policy to one Flask view decorate-route

from flask_cors import cross_origin
@app.post("/partner/import")
@cross_origin(origins=["https://partner.example"], allow_headers=["Content-Type", "X-Signature"])
def partner_import(): return {"accepted": True}, 202

Place `cross_origin` directly below Flask's route decorator so both wrappers apply to the same function.

Attach origin rules to a Blueprint configure-blueprint

from flask import Blueprint
from flask_cors import CORS
api = Blueprint("api", __name__, url_prefix="/api")
CORS(api, origins=["https://app.example.com"])
app.register_blueprint(api)

Blueprint annotations work under strict MyPy in 6.0.5, the patch that repaired the 6.0.4 typing regression.

Enable a credentialed session endpoint allow-cookies

CORS(app, resources={r"/api/session/*": {
 "origins": ["https://app.example.com"],
 "supports_credentials": True
}})

The client must request credentials, and a cross-site cookie normally needs `SameSite=None` plus `Secure`.

Let JavaScript read response metadata expose-metadata

CORS(app, origins=["https://app.example.com"], expose_headers=["X-Total-Count", "X-Request-Id"])

A custom response header can be visible in browser tools yet unavailable to JavaScript until exposed here.

Set a one-hour preflight lifetime cache-options

from datetime import timedelta
CORS(app, origins=["https://app.example.com"], methods=["GET", "POST"], max_age=timedelta(hours=1))

Browsers may cap this 1-hour value, and already cached preflights do not disappear when server policy changes.

Trace which resource rule matched debug-preflight

import logging
logging.getLogger("flask_cors").setLevel(logging.DEBUG)

The logger reports rule selection and emitted headers; disable verbose logging if request origins contain sensitive tenant data.

Alternatives

PackageRegistryPick it when
starlettePyPIUse its CORSMiddleware in Starlette and FastAPI ASGI applications.
django-cors-headersPyPIUse it when Django middleware and settings own the request path.
quart-corsPyPIUse it for the async Quart framework and its response lifecycle.

More web backend guides

urllib3 · requests · ws · anyio · httpx · undici · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.