FastAPI CORS: let your frontend read the API response
Allow one exact frontend origin in FastAPI, reproduce browser CORS behavior locally, and avoid treating CORS as authentication.
In this guide
If your FastAPI endpoint opens directly but a page on another address cannot read it, check CORS. This browser rule controls whether a page from one origin may read a response from another.
An origin is the combination of scheme, host and port. http://127.0.0.1:8001 and http://127.0.0.1:8000 are different origins because their ports differ. localhost and 127.0.0.1 are also different hosts, even when both reach your computer.
For an existing app, add your frontend’s exact origin to CORSMiddleware, then restart the API and test from that frontend. The optional local rehearsal below makes the distinction visible without buying hosting.
Allow the page that actually makes the request
Use the page’s origin from its browser address, including the port when present, without a path or trailing slash. Do not use the API’s own origin merely because that is the address returning the data.
The complete example below allows http://127.0.0.1:8001 to read a public GET endpoint. It uses no login cookie or authorization header. In your existing app, add the import and middleware after creating app; keep your existing routes rather than replacing them with this sample.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://127.0.0.1:8001"],
allow_methods=["GET"],
allow_headers=[],
)
@app.get("/workshop")
def workshop():
return {"name": "Python basics", "available_seats": 6}
For a real frontend, substitute its actual origin and list the methods and additional headers it needs. Do not add * as a quick fix for a private application. Requests with credentials need an explicit configuration; this no-credentials example is not a cookie-login setup.
Optional: run the two sides locally
Use Windows PowerShell, Python 3.12, FastAPI 0.143.0 and Uvicorn 0.54.0. If your app and frontend already run, skip this rehearsal and test them directly.
Otherwise, create an empty folder named cors-practice. Open PowerShell there from File Explorer’s address bar. Check python --version; if Python is missing, install it from official downloads. Create an environment:
python -m venv .venv
After it succeeds, install the packages:
.\.venv\Scripts\python.exe -m pip install fastapi==0.143.0 uvicorn==0.54.0
Save the Python example above as main.py in that folder. Save this as index.html beside it, using a plain-text editor and exact filenames:
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Workshop availability — CORS test</title>
<h1>Check workshop availability</h1>
<button id="check">Check available seats</button>
<pre id="result">Press the button to request data from the API.</pre>
<script>
document.querySelector('#check').addEventListener('click', async () => {
const output = document.querySelector('#result');
try {
const response = await fetch('http://127.0.0.1:8000/workshop');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
output.textContent = JSON.stringify(await response.json(), null, 2);
} catch (error) {
output.textContent = `Request failed: ${error.message}. Check the API and CORS settings.`;
}
});
</script>
</html>
Start the API in the first terminal:
.\.venv\Scripts\python.exe -m uvicorn main:app --host 127.0.0.1 --port 8000
Leave it running. Open a second PowerShell window in the same folder and serve the practice HTML:
.\.venv\Scripts\python.exe -m http.server 8001 --bind 127.0.0.1
Use an empty practice folder, not one containing private files: this second command serves that folder locally. If either port is occupied, do not stop an unknown process. Choose unused ports and update the middleware origin, fetch URL and browser address consistently.
Verify reading from the allowed origin
Open http://127.0.0.1:8001/ and click Check available seats. Expect the workshop JSON with six available seats. Do not open index.html by double-clicking it: a file: page has a different origin.
Now open the same page at http://localhost:8001/ and press the button. In this configuration it is not allowed: expect Request failed. The API can still receive the request and return 200, while the browser refuses to expose its response to this page. That difference is why a successful curl request or API log is not a CORS test.
In a real app, open browser developer tools with F12, check Console for the CORS message and Network for the request. Some requests first send OPTIONS, a preflight asking whether the method and headers are allowed. Check the origin, method and headers from that request against your middleware settings.
If the error remains
Wrong host, scheme or port: compare the complete frontend origin. HTTP and HTTPS differ; so do localhost and 127.0.0.1. Add only the actual origins your app should support.
Connection refused or 404: check that the API is running and the path exists. CORS settings cannot start a server or invent a route.
Edits appear to do nothing: save the file, stop the API with Ctrl+C, and start it again. The command above does not reload automatically. A proxy in front of a deployed app may also generate a response before FastAPI sees it; inspect the response’s actual source.
A preflight fails: allow the required method and nonstandard request headers explicitly. Do not enable credentials unless the frontend really uses them and the authentication design calls for it.
CORS is not authentication. It does not stop a non-browser client from requesting your API or protect a secret endpoint from unauthorized users.
For the rehearsal, press Ctrl+C in both terminal windows and wait for their prompts. No external service or server was created.
Sources and useful links
- FastAPI CORS — exact origins, credentials and preflight behavior.
- MDN CORS guide — the browser’s response-sharing checks.
- Python http.server — the loopback HTML server used only for this rehearsal.
- Official Python downloads — interpreter setup if needed.