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-Origin and related headers on the API server, not in client code.
  • Key source files: In psf/requests, the src/requests/api.py entry point, src/requests/models.py header handling, and src/requests/sessions.py dispatch logic confirm that the library does not process CORS headers.
  • Framework-specific solutions: Use flask_cors for Flask, custom middleware for Django, CORSMiddleware for 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →