How It Works Features Pricing Blog Error Guides
Log In Start Free Trial
FastAPI · Python

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

Traceback (most recent call last): File "/app/venv/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 406, in run_asgi result = await app(scope, receive, send) File "/app/venv/lib/python3.11/site-packages/starlette/middleware/__init__.py", line 24, in __call__ await self.app(scope, receive, send) File "/app/venv/lib/python3.11/site-packages/starlette/routing.py", line 677, in __call__ await route.handle(scope, receive, send) File "/app/venv/lib/python3.11/site-packages/starlette/routing.py", line 275, in handle await self.app(scope, receive, send) File "/app/venv/lib/python3.11/site-packages/fastapi/routing.py", line 234, in app raw_response = await run_endpoint_function(dependant=dependant, values=values) File "/app/src/main.py", line 24, in get_data return {"data": results} # Browser Console: Access to fetch at 'http://localhost:8000/api/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Here's what each line means:

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.

Before (broken)
from fastapi import FastAPI

app = FastAPI()

@app.get("/api/data")
async def get_data():
    return {"data": ["item1", "item2"]}
After (fixed)
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:

  1. Notice the error alert or see it in your monitoring tool
  2. Open the error dashboard and read the stack trace
  3. Identify the file and line number from the stack trace
  4. Open your IDE and navigate to the file
  5. Read the surrounding code to understand context
  6. Reproduce the error locally
  7. Identify the root cause
  8. Write the fix
  9. Run the test suite locally
  10. Fix any failing tests
  11. Write new tests covering the edge case
  12. Run the full test suite again
  13. Create a new git branch
  14. Commit and push your changes
  15. Open a pull request
  16. Wait for code review
  17. Merge and deploy to production
  18. 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:

  1. Captures the stack trace and request context
  2. Pulls the relevant source files from your GitHub repo
  3. Analyzes the error and understands the code context
  4. Generates a minimal, verified fix
  5. Runs your existing test suite
  6. Pushes through your CI/CD pipeline
  7. 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)

  1. Run the test suite locally to confirm CORS headers are present.
  2. Open a pull request with the CORSMiddleware addition.
  3. Wait for CI checks to pass on the PR.
  4. Have a teammate review and approve the PR.
  5. 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.