CORS — browser ↔ API
How cross-origin requests work for dwd-api-fastapi behind Nginx on msi-laptop-3.
Standard answer: configure CORS in the Python app
| Layer | Handle CORS? | When |
|---|---|---|
FastAPI (CORSMiddleware) | Yes — primary | Browser calls API on a different origin (port, host, or scheme) |
| Nginx | Rarely | Only if the backend cannot set headers (legacy app, static files) |
This API already uses app-level CORS in config.py / main.py:
app.add_middleware(CORSMiddleware, **CORS_CONFIG)
Nginx should pass through Access-Control-* headers from the upstream — do not duplicate CORS in Nginx unless you remove it from the app (avoid double headers).
Why not Nginx-only?
- Preflight
OPTIONSmust return correctAccess-Control-Allow-Methods,Allow-Headers, and oftenAccess-Control-Allow-Credentials - With credentials,
Allow-Originmust be the specific requesting origin, not* - Per-environment origin lists are easier in code or env vars than maintaining nginx
mapblocks
Allowed origins today
From repos/dwd-api-fastapi/config.py:
| Origin | Purpose |
|---|---|
http://localhost:3000 | Local React dev (Vite/CRA) |
http://localhost:3001, 3002 | Alt dev ports |
http://127.0.0.1:3000, … | Same, 127.0.0.1 |
https://dwd.hydrotrek.com, … | Production SPAs |
Various 192.168.50.x | Prior LAN hosts |
Your current dev setup: browser origin http://localhost:3000 → API http://192.168.0.85 — localhost:3000 is already in the list. CORS is configured correctly for that pair.
When you deploy a SPA to a new hostname, add that exact origin (scheme + host + port) to CORS_ORIGINS in config.py, rebuild/redeploy the container, and redeploy.
Production example to add later:
"https://dwd-api.yourdomain.com", # if SPA hosted here
"https://app.yourdomain.com",
Your error: CORS message + 500 — two layers
Browser console:
No 'Access-Control-Allow-Origin' header ...
GET http://192.168.0.85/graph-data?...base_url=https://localhost:7033/... 500
1. Primary issue: 500 Internal Server Error
The API on the server calls the ASP.NET backend using base_url from the query string:
base_url=https://localhost:7033/api/
Inside Docker on 192.168.0.85, localhost is the container, not your Windows PC. The fetch to ASP.NET fails → 500.
Fix: pass a URL the server can reach, e.g. your workstation’s LAN IP:
base_url=https://192.168.0.XX:7033/api/
Requirements:
- ASP.NET listens on
0.0.0.0:7033(or LAN IP), not only127.0.0.1 - Windows firewall allows inbound 7033 from the server
- HTTPS cert trust if using
https://(API may disable verify for localhost — checkcreate_httpx_clientinmain.py)
Or set DWD_API_BASE_URL in /etc/dwd-api-fastapi/dwd-api-fastapi.env to that reachable URL and omit base_url from the frontend.
2. CORS message is often misleading on failed responses
Browsers report CORS when the response lacks Access-Control-Allow-Origin. That happens when:
- Nginx returns its own error page (502/504)
- The app crashes before middleware adds headers (uncommon with FastAPI CORS on route errors)
- Nginx returns 301/302 on OPTIONS preflight (e.g. trailing-slash redirect on
/gqc/api/copilotkit→/gqc/api/copilotkit/). Browsers fail with: "Redirect is not allowed for a preflight request". Fix: proxy the exact path without redirecting; see 15-api-reverse-proxy-base-paths-todo.md.
Fix the 500 first. Test from the server:
curl -sS -D- "http://127.0.0.1:8001/graph-data?project_id=...&scenario_id=..." \
-H "Origin: http://localhost:3000" | head -30
Look for HTTP/1.1 200 and access-control-allow-origin: http://localhost:3000.
Verify CORS (after API returns 200)
From Windows (replace query params with valid test values):
curl -v "http://192.168.0.85/health" -H "Origin: http://localhost:3000"
Expect:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: true
Preflight (if frontend sends non-simple headers):
curl -v -X OPTIONS "http://192.168.0.85/graph-data" `
-H "Origin: http://localhost:3000" `
-H "Access-Control-Request-Method: GET"
Expect 204 or 200 with Access-Control-Allow-Methods.
Nginx (reference only — not needed for this API)
If you ever had to do CORS in Nginx without app support:
location / {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "*";
return 204;
}
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials true always;
proxy_pass http://backend;
}
Use $http_origin with a map whitelist in production — do not reflect arbitrary origins. Prefer app-level CORS for FastAPI.
Checklist — new frontend origin
- Add exact origin string to
CORS_ORIGINSinconfig.py - Rebuild/restart API container
- Confirm
curl -H "Origin: ..." http://192.168.0.85/healthshowsAccess-Control-Allow-Origin - Ensure
base_url/DWD_API_BASE_URLis reachable from the server, notlocalhoston your PC
Related
- API env:
/etc/dwd-api-fastapi/dwd-api-fastapi.env—DWD_API_BASE_URL - Nginx LAN: 11-nginx-lan-setup.md
- Subdomain prod: 04-nginx-reverse-proxy.md