How to Configure CORS Headers for Cross-Origin API Requests: A Server-Side Guide
Cross-Origin Resource Sharing (CORS) errors occur when browsers block API responses that lack proper Access-Control-Allow-Origin headers, and these headers must be configured on the server hosting the API rather than in client-side HTTP libraries like Python requests.
When your web application attempts to fetch data from an API on a different domain, the browser enforces CORS policies that prevent access unless the server explicitly permits cross-origin requests. This guide explains how to properly configure CORS headers using popular Python frameworks, while referencing the psf/requests source code to clarify why the HTTP client library cannot resolve these browser-enforced restrictions.
Understanding Why Browsers Block Cross-Origin Requests
CORS is a security mechanism enforced by client-side browsers, not by HTTP client libraries. When a browser sends a request to a different origin (domain, protocol, or port), it first performs a pre-flight OPTIONS request for non-simple requests, or checks the response headers of simple requests. If the server response does not contain the required Access-Control-Allow-Origin header, the browser blocks the response entirely and displays the CORS policy error.
The Python requests library, as implemented in src/requests/api.py, functions as a generic HTTP client that builds outgoing requests and handles responses, but it operates outside the browser's security sandbox. The library's public request() function delegates to a Session object defined in src/requests/sessions.py, while header handling logic resides in src/requests/models.py within the Request object. None of these modules add or enforce CORS headers because CORS validation occurs after the response leaves the server and reaches the browser.
Why the Requests Library Cannot Configure CORS Headers
The requests library architecture demonstrates why CORS configuration is impossible from the client side. In src/requests/api.py, the library provides the entry point for HTTP calls but focuses solely on constructing valid HTTP requests and parsing responses. The Request class in src/requests/models.py stores headers as generic key-value pairs without any CORS-specific logic, and src/requests/sessions.py manages connection pooling and request dispatch.
Since CORS policies are enforced by the browser's rendering engine after receiving the HTTP response, no amount of client-side header manipulation in requests can bypass the restriction. The browser requires specific Access-Control-Allow-* headers in the server's response to grant permission for cross-origin access.
How to Configure CORS Headers on Your API Server
To resolve CORS errors, you must modify the server hosting the API to emit the appropriate headers. Below are implementations for common Python web frameworks.
Flask
Use the flask_cors extension to automatically inject CORS headers and handle pre-flight OPTIONS requests:
from flask import Flask, jsonify
from flask_cors import CORS
app = Flask(__name__)
# Enable CORS for all routes with Access-Control-Allow-Origin: *
CORS(app)
@app.route('/data')
def data():
return jsonify(message='Hello from Flask')
Django
Create custom middleware to inject headers into every response:
# myproject/middleware.py
class SimpleCORS:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS"
response["Access-Control-Allow-Headers"] = "Content-Type,Authorization"
return response
Add 'myproject.middleware.SimpleCORS' to the MIDDLEWARE list in settings.py.
FastAPI
Leverage the built-in CORSMiddleware to configure CORS policies declaratively:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Or specify ["https://example.com"]
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/items")
async def read_items():
return {"msg": "FastAPI CORS enabled"}
Generic WSGI Applications
For frameworks without built-in CORS support, wrap your application with a middleware that modifies the response headers:
def simple_cors_middleware(app):
def wrapper(environ, start_response):
def cors_start_response(status, headers, exc_info=None):
headers.append(("Access-Control-Allow-Origin", "*"))
headers.append(("Access-Control-Allow-Methods", "GET,POST,OPTIONS"))
headers.append(("Access-Control-Allow-Headers", "Content-Type,Authorization"))
return start_response(status, headers, exc_info)
return app(environ, cors_start_response)
return wrapper
Apply this wrapper around any WSGI-compatible application to inject CORS headers into every response.
Verifying CORS Configuration with the Requests Library
Although requests cannot bypass browser CORS restrictions, you can use it to verify that your server sends the correct headers before testing in a browser:
import requests
# Verify pre-flight headers
resp = requests.options('https://api.example.com/data')
print(resp.headers.get('Access-Control-Allow-Origin')) # Expected: '*'
# Verify actual response headers
resp = requests.get('https://api.example.com/data')
print(resp.headers.get('Access-Control-Allow-Methods')) # Expected: 'GET,POST,OPTIONS'
If these headers appear in the requests response but browsers still block your application, ensure your server handles the OPTIONS pre-flight method and returns a 200 OK status for those requests.
Summary
- CORS is browser-enforced: The restriction occurs in the browser's security model, not in HTTP client libraries like
requests. - Server-side configuration required: You must configure
Access-Control-Allow-Originand related headers on the API server, not in client code. - Key source files: In
psf/requests, thesrc/requests/api.pyentry point,src/requests/models.pyheader handling, andsrc/requests/sessions.pydispatch logic confirm that the library does not process CORS headers. - Framework-specific solutions: Use
flask_corsfor Flask, custom middleware for Django,CORSMiddlewarefor FastAPI, or WSGI wrappers for generic applications. - Testing approach: Use
requests.options()to verify headers are present before browser testing.
Frequently Asked Questions
Why does my Python script work but my browser shows a CORS error?
Python scripts using the requests library operate outside the browser's security sandbox and do not enforce CORS policies. Browsers implement Same-Origin Policy protections that block responses lacking Access-Control-Allow-Origin headers, which is why the same API call succeeds in Python but fails in JavaScript fetch or XMLHttpRequest.
Can I add CORS headers to my requests using the requests library to fix the error?
No. Adding headers like Access-Control-Allow-Origin to your outgoing request in requests (via headers= parameter) has no effect because CORS headers must be present in the server's response, not the client's request. The browser blocks the response before your JavaScript code can access it if the server fails to send the proper headers.
What is the minimum CORS configuration needed to allow cross-origin requests?
At minimum, the server must return the Access-Control-Allow-Origin header with either a specific origin (e.g., https://yourdomain.com) or a wildcard (*). For requests with custom headers or non-simple methods (PUT, DELETE), the server must also handle OPTIONS pre-flight requests and return Access-Control-Allow-Methods and Access-Control-Allow-Headers.
How do I configure CORS headers for specific routes only rather than all API endpoints?
Most frameworks support route-specific CORS configuration. In Flask, use the @cross_origin() decorator on specific view functions instead of applying CORS(app) globally. In FastAPI, apply the CORSMiddleware to specific routers or use the allow_origins parameter to restrict which domains can access particular endpoints.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →