Flask
CORS
web development
API
tutorial

How to enable CORS in flask

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Browsers block many cross-origin requests by default, even when your Flask API is working correctly. CORS, short for Cross-Origin Resource Sharing, tells the browser which origins, methods, and headers are allowed. In Flask, the safest setup is usually a narrow allowlist rather than a global wildcard.

Enable CORS with Flask-CORS

For most projects, the Flask-CORS package is the easiest way to configure the correct headers. It handles normal responses and preflight OPTIONS requests without much boilerplate.

Install it with:

bash
pip install flask-cors

Then configure allowed origins for only the routes that need them:

python
1from flask import Flask, jsonify
2from flask_cors import CORS
3
4app = Flask(__name__)
5
6CORS(
7    app,
8    resources={
9        r"/api/*": {
10            "origins": ["https://app.example.com"],
11            "methods": ["GET", "POST", "PUT", "DELETE"],
12            "allow_headers": ["Content-Type", "Authorization"],
13        }
14    },
15)
16
17
18@app.get("/api/health")
19def health():
20    return jsonify(status="ok")
21
22
23if __name__ == "__main__":
24    app.run(debug=True)

This setup allows your frontend at https://app.example.com to call routes under /api/. Requests from other origins will still be blocked by the browser.

If your frontend sends cookies or authorization tied to browser credentials, you also need to opt in:

python
1CORS(
2    app,
3    resources={r"/api/*": {"origins": ["https://app.example.com"]}},
4    supports_credentials=True,
5)

When credentials are enabled, do not use "*" as the allowed origin. Browsers reject that combination because it is too permissive.

Add CORS Headers Manually When You Need Tight Control

If you do not want an extension, you can add the headers yourself. This is useful when the allowlist depends on request data or when you want to understand exactly what the server is returning.

python
1from flask import Flask, jsonify, request
2
3app = Flask(__name__)
4ALLOWED_ORIGINS = {"https://app.example.com"}
5
6
7@app.after_request
8def add_cors_headers(response):
9    origin = request.headers.get("Origin")
10    if origin in ALLOWED_ORIGINS:
11        response.headers["Access-Control-Allow-Origin"] = origin
12        response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
13        response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
14        response.headers["Vary"] = "Origin"
15    return response
16
17
18@app.route("/api/items", methods=["GET", "POST", "OPTIONS"])
19def items():
20    if request.method == "OPTIONS":
21        return ("", 204)
22    return jsonify(items=["book", "pen"])
23
24
25if __name__ == "__main__":
26    app.run(debug=True)

The Vary header matters here. Setting Vary: Origin tells caches that the response may differ by requesting origin, which prevents one origin from receiving headers intended for another.

How Preflight Requests Fit In

Browsers send a preflight OPTIONS request before some cross-origin calls, especially when the request uses custom headers or non-simple methods such as PUT or DELETE. Your server must answer that preflight with the correct CORS headers or the browser will block the real request before it is even sent.

That is why CORS issues can be confusing: your Flask route may work with curl, Postman, or a backend client, but still fail in the browser. CORS is enforced by the browser, not by Flask itself.

Common Pitfalls

The most common mistake is allowing every origin with "*" in production. That is often broader than necessary and becomes incompatible with credentialed requests.

Another mistake is forgetting about preflight handling. If the browser sends OPTIONS and your app returns a 405 response or misses the right headers, the real request never proceeds.

Many teams also treat CORS as an authentication system. It is not. CORS only tells browsers whether cross-origin JavaScript may read a response. It does not stop direct requests from non-browser clients, so you still need proper authentication and authorization.

Finally, be careful when reflecting the request origin dynamically. If you mirror any incoming Origin header without validation, you have effectively disabled origin restrictions.

Summary

  • CORS controls which browser-based cross-origin requests may read your Flask responses.
  • 'Flask-CORS is the quickest way to configure route-specific CORS behavior.'
  • Manual headers are useful when you need stricter or more dynamic control.
  • Credentialed requests require an explicit origin, not "*".
  • Successful API calls in Postman do not prove CORS is configured correctly for browsers.

Course illustration
Course illustration

All Rights Reserved.