Skip to main content

Flask CORS Configuration

Cross-Origin Resource Sharing (CORS) is a crucial security mechanism that restricts how resources on a web page can be requested from another domain. In this tutorial, we'll explore how to configure CORS in Flask applications to ensure your APIs are accessible only to authorized domains.

What is CORS?

CORS (Cross-Origin Resource Sharing) is a security feature implemented by browsers that restricts web pages from making requests to a different domain than the one that served the original page. This is known as the "Same-Origin Policy" and helps prevent malicious websites from accessing sensitive data on other sites.

However, there are legitimate scenarios where you want to allow cross-origin requests, such as:

  • Building a public API that serves multiple domains
  • Creating a microservices architecture where services communicate across domains
  • Developing a frontend separate from your API backend

Why Configure CORS in Flask?

By default, Flask doesn't implement CORS protection. This means your Flask API could potentially be accessed by any website, which poses security risks. Configuring CORS in Flask allows you to:

  1. Specify which domains can access your API
  2. Control which HTTP methods are allowed (GET, POST, etc.)
  3. Manage whether credentials (cookies, HTTP authentication) can be included in requests
  4. Set how long browsers should cache CORS responses

Using Flask-CORS Extension

The easiest way to implement CORS in Flask is using the flask-cors extension. Let's see how to install and use it:

Installation

bash
pip install flask-cors

Basic Usage

Here's a simple example of implementing CORS in a Flask application:

python
from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)

# Enable CORS for all routes
CORS(app)

@app.route('/api/data')
def get_data():
return jsonify({"message": "This endpoint can be accessed from any origin"})

if __name__ == '__main__':
app.run(debug=True)

When you run this application and make a request from a different domain, the browser will receive the appropriate CORS headers that allow the request to succeed.

Configuring CORS Options

In real-world applications, you'll want finer control over CORS settings. Here are common configurations:

Restricting to Specific Origins

python
from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)

# Allow CORS only for specific origins
CORS(app, resources={
r"/api/*": {"origins": ["https://example.com", "https://subdomain.example.com"]}
})

@app.route('/api/protected-data')
def get_protected_data():
return jsonify({"message": "This endpoint can only be accessed from allowed origins"})

if __name__ == '__main__':
app.run(debug=True)

Configuring Specific Routes

You can apply different CORS settings to different routes:

python
from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)

# Public API - allow all origins
CORS(app, resources={r"/api/public/*": {"origins": "*"}})

# Private API - restrict to specific origins
CORS(app, resources={r"/api/private/*": {"origins": "https://example.com"}})

# Internal API - no CORS (same-origin only)
# No CORS configuration for /api/internal/*

@app.route('/api/public/data')
def get_public_data():
return jsonify({"message": "Public API - accessible from anywhere"})

@app.route('/api/private/data')
def get_private_data():
return jsonify({"message": "Private API - restricted access"})

@app.route('/api/internal/data')
def get_internal_data():
return jsonify({"message": "Internal API - same origin only"})

if __name__ == '__main__':
app.run(debug=True)

Advanced CORS Configuration

For more complex scenarios, you can set additional CORS options:

python
from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)

# Advanced CORS configuration
CORS(app, resources={r"/api/*": {
"origins": ["https://example.com"], # Allowed origins
"methods": ["GET", "POST", "PUT"], # Allowed methods
"allow_headers": ["Content-Type", "Authorization"], # Allowed headers
"max_age": 3600, # Cache CORS response for 1 hour
"supports_credentials": True # Allow cookies to be sent
}})

@app.route('/api/user', methods=['GET', 'POST', 'PUT'])
def user_endpoint():
return jsonify({"message": "User endpoint with custom CORS configuration"})

if __name__ == '__main__':
app.run(debug=True)

Implementing CORS Per-Route with Decorators

You can also apply CORS settings on a per-route basis using decorators:

python
from flask import Flask, jsonify
from flask_cors import cross_origin

app = Flask(__name__)

@app.route('/api/default')
def default_endpoint():
# Uses global CORS settings (or none if not specified)
return jsonify({"message": "Default CORS settings"})

@app.route('/api/custom')
@cross_origin(origins=["https://special-client.com"], methods=["GET"])
def custom_cors_endpoint():
# This route has its own CORS settings
return jsonify({"message": "Custom CORS settings for this route only"})

if __name__ == '__main__':
app.run(debug=True)

Testing CORS Configuration

To test your CORS configuration, you can use tools like curl or create a simple HTML page with JavaScript fetch:

html
<!DOCTYPE html>
<html>
<head>
<title>CORS Test</title>
</head>
<body>
<h1>Testing CORS Configuration</h1>
<button onclick="testCORS()">Test API Access</button>
<div id="result"></div>

<script>
function testCORS() {
fetch('http://localhost:5000/api/data', {
method: 'GET',
credentials: 'include' // Include cookies if needed
})
.then(response => response.json())
.then(data => {
document.getElementById('result').innerText =
'Success! Response: ' + JSON.stringify(data);
})
.catch(error => {
document.getElementById('result').innerText =
'Error: ' + error.message;
});
}
</script>
</body>
</html>

Save this as a file and open it in your browser. When you click the button, you'll see whether your CORS configuration allows the request.

Real-World Example: Secure API with CORS

Here's a more complete example of a Flask API with proper CORS configuration:

python
from flask import Flask, jsonify, request
from flask_cors import CORS
import os

app = Flask(__name__)

# Environment-based configuration
if os.environ.get('FLASK_ENV') == 'development':
# During development, allow requests from localhost
CORS(app, resources={r"/api/*": {
"origins": ["http://localhost:3000", "http://127.0.0.1:3000"]
}})
else:
# In production, restrict to specific domains
CORS(app, resources={r"/api/*": {
"origins": ["https://production-frontend.example.com"],
"methods": ["GET", "POST", "PUT", "DELETE"],
"allow_headers": ["Content-Type", "Authorization"],
"supports_credentials": True
}})

# Public endpoints accessible by anyone
CORS(app, resources={r"/api/public/*": {"origins": "*"}})

@app.route('/api/user/profile', methods=['GET'])
def get_user_profile():
# Protected endpoint - only accessible from allowed origins
# Authentication logic would go here
user_id = request.args.get('id', 'default')
return jsonify({
"id": user_id,
"name": "John Doe",
"email": "[email protected]"
})

@app.route('/api/public/status', methods=['GET'])
def get_api_status():
# Public endpoint - accessible from anywhere
return jsonify({
"status": "operational",
"version": "1.0.0"
})

if __name__ == '__main__':
app.run(debug=True)

Security Considerations

When configuring CORS, keep these security best practices in mind:

  1. Avoid using origins: "*" in production unless you're building a truly public API
  2. Only allow necessary HTTP methods for each endpoint
  3. Be cautious with supports_credentials: True as it allows cookies to be sent cross-origin
  4. Regularly review and update your CORS configuration as your application evolves
  5. Use environment variables to manage different CORS settings in development vs. production

Common CORS Issues and Solutions

Preflight Requests

For complex requests (with custom headers, non-simple methods), browsers send a preflight OPTIONS request:

python
@app.route('/api/data', methods=['GET', 'POST', 'OPTIONS'])
@cross_origin(origins=["https://example.com"])
def handle_data():
# The OPTIONS method is handled automatically by flask-cors
if request.method == 'GET':
return jsonify({"message": "Data retrieved"})
elif request.method == 'POST':
return jsonify({"message": "Data created"})

Missing Headers

If you're still experiencing CORS errors, check that all necessary headers are being set:

python
from flask import Flask, jsonify, make_response

app = Flask(__name__)

@app.route('/api/manual-cors')
def manual_cors():
response = make_response(jsonify({"message": "Manual CORS response"}))
response.headers.add('Access-Control-Allow-Origin', 'https://example.com')
response.headers.add('Access-Control-Allow-Methods', 'GET')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
return response

Summary

Configuring CORS properly in your Flask application is essential for building secure web services. In this tutorial, we've covered:

  • What CORS is and why it's important
  • How to install and use the flask-cors extension
  • Configuring CORS for specific origins, routes, and methods
  • Using decorators for per-route CORS settings
  • Testing CORS configurations
  • Security best practices for CORS implementation

By implementing proper CORS settings, you can ensure your Flask APIs are accessible to legitimate clients while maintaining security against cross-site scripting attacks.

Additional Resources

Exercises

  1. Create a Flask application with two endpoints: one accessible from any origin and another restricted to a single domain.
  2. Modify a Flask API to allow CORS only for specific HTTP methods (GET and POST, but not PUT or DELETE).
  3. Configure a Flask application with different CORS settings for development and production environments.
  4. Build a simple frontend that tests accessing your CORS-protected API from a different domain.
  5. Implement proper error handling for CORS preflight requests that fail validation.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)