Fix CORSError: Access to fetch at 'http://localhost:8000/api/data' from origin 'http://localhost:3000' has been blocked by CORS policy in FastAPI
This error occurs when a frontend running on a different origin makes a request to your FastAPI backend and the server does not include the required CORS headers. Fix it by adding CORSMiddleware to your FastAPI app with the correct allowed origins, methods, and headers so the browser permits the cross-origin request.
Reading the Stack Trace
Here's what each line means:
- File "/app/venv/lib/python3.11/site-packages/starlette/middleware/__init__.py", line 24, in __call__: The middleware stack processes the request but no CORSMiddleware is configured to add the required headers.
- File "/app/src/main.py", line 24, in get_data: The endpoint executes successfully, but the browser rejects the response because CORS headers are missing.
- No 'Access-Control-Allow-Origin' header is present on the requested resource.: The browser enforces same-origin policy and blocks the response since the server did not whitelist the requesting origin.
Common Causes
1. CORSMiddleware not added to the app
The FastAPI application was created without adding CORSMiddleware, so no CORS headers are ever sent in responses.
from fastapi import FastAPI
app = FastAPI()
@app.get("/api/data")
async def get_data():
return {"data": ["item1", "item2"]}
2. Incorrect allowed origins
CORSMiddleware is added but the frontend origin is not included in the allow_origins list.
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:8000"], # Wrong origin
allow_methods=["GET"],
)
3. Missing allow_headers for custom headers
The frontend sends custom headers like Authorization but allow_headers does not include them.
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_methods=["*"],
# allow_headers defaults to [], blocking Authorization header
)
The Fix
Add CORSMiddleware to the FastAPI app with the frontend origin in allow_origins. Setting allow_methods and allow_headers to '*' permits all HTTP methods and headers. In production, restrict these to only the origins, methods, and headers your frontend actually uses.
from fastapi import FastAPI
app = FastAPI()
@app.get("/api/data")
async def get_data():
return {"data": ["item1", "item2"]}
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api/data")
async def get_data():
return {"data": ["item1", "item2"]}
Testing the Fix
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_cors_preflight_request():
response = client.options(
"/api/data",
headers={
"Origin": "http://localhost:3000",
"Access-Control-Request-Method": "GET",
},
)
assert response.status_code == 200
assert "http://localhost:3000" in response.headers.get(
"access-control-allow-origin", ""
)
def test_cors_header_in_get_response():
response = client.get(
"/api/data",
headers={"Origin": "http://localhost:3000"},
)
assert response.status_code == 200
assert response.headers["access-control-allow-origin"] == "http://localhost:3000"
def test_cors_blocked_for_unknown_origin():
response = client.get(
"/api/data",
headers={"Origin": "http://evil-site.com"},
)
assert "access-control-allow-origin" not in response.headers
Run your tests:
pytest tests/test_cors.py -v
Pushing Through CI/CD
git checkout -b fix/fastapi-cors-middleware,git add src/main.py tests/test_cors.py,git commit -m "fix: add CORSMiddleware with correct allowed origins",git push origin fix/fastapi-cors-middleware
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- run: pip install -r requirements.txt
- run: pytest --tb=short -q
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
pip install bugstack
Step 2: Initialize
import bugstack
bugstack.init(api_key=os.environ["BUGSTACK_API_KEY"])
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Run the test suite locally to confirm CORS headers are present.
- Open a pull request with the CORSMiddleware addition.
- Wait for CI checks to pass on the PR.
- Have a teammate review and approve the PR.
- Merge to main and verify cross-origin requests work in staging.
Frequently Asked Questions
BugStack tests both preflight OPTIONS requests and actual GET/POST requests from the expected origins, verifies the correct headers are present, and confirms no unauthorized origins are allowed.
BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review the CORS configuration before merging.
No. Using '*' allows any website to make requests to your API. In production, list only your actual frontend domains to prevent unauthorized cross-origin access.
CORS is enforced by browsers, not servers. Tools like curl or Postman do not enforce same-origin policy, so the request works there but fails in the browser.