CORS Configuration - Standard Operating Procedure
Last Updated: 2026-01-02
Status: Production
This SOP documents the CORS configuration for Engram and how to diagnose and fix CORS errors.
Quick Fix Checklist
If you see “No ‘Access-Control-Allow-Origin’ header is present”:
-
Verify
CORS_ORIGINSincludes your frontend URLaz containerapp show --name staging-env-api --resource-group engram-rg \ --query "properties.template.containers[0].env[?name=='CORS_ORIGINS']" -o tableMust include:
https://engram.work -
Verify Azure Platform Auth is DISABLED
az containerapp auth show --name staging-env-api --resource-group engram-rgMust return:
"platform": { "enabled": false }If enabled:
az containerapp auth update --name staging-env-api --resource-group engram-rg \ --enabled false --action AllowAnonymous -
Verify
CORSPreflightMiddlewareis registered inmain.pygrep -n "CORSPreflightMiddleware" backend/api/main.pyMust show import AND
app.add_middleware(CORSPreflightMiddleware) -
If missing, add the middleware:
from .middleware.cors_preflight import CORSPreflightMiddleware # ... later in create_app() ... app.add_middleware(CORSPreflightMiddleware) -
Deploy and test
git add . && git commit -m "fix(cors): ensure middleware is registered" git push
Architecture
Middleware Stack (Execution Order)
Request → ProxyHeaders → Telemetry → Logging → CORSPreflight → CORSMiddleware → Routes
| Middleware | Purpose |
|---|---|
CORSMiddleware | FastAPI built-in, adds CORS headers to responses |
CORSPreflightMiddleware | Intercepts OPTIONS requests before auth |
RequestLoggingMiddleware | Logs request/response details |
TelemetryMiddleware | OpenTelemetry tracing |
ProxyHeadersMiddleware | Handles X-Forwarded-* from load balancer |
Key Files
| File | Purpose |
|---|---|
backend/api/main.py | Middleware registration |
backend/api/middleware/cors_preflight.py | OPTIONS handler |
backend/core/config.py | CORS_ORIGINS setting |
infra/modules/backend-aca.bicep | Azure deployment config |
Configuration
Environment Variable: CORS_ORIGINS
Format: JSON array as string
Production value:
["https://engram.work","http://localhost:5173","http://localhost:5174"]
Set in: infra/modules/backend-aca.bicep line 277-279
Code default: Only localhost (see config.py line 140)
Common Issues
Issue 1: Middleware Not Registered
Symptom: CORS errors despite correct CORS_ORIGINS
Cause: CORSPreflightMiddleware exists but not imported/added
Fix: Add to main.py:
from .middleware.cors_preflight import CORSPreflightMiddleware
# ... in create_app() ...
app.add_middleware(CORSPreflightMiddleware)
Issue 2: Missing Origin in CORS_ORIGINS
Symptom: CORS works locally but fails in production
Cause: Production URL not in CORS_ORIGINS
Fix: Update Bicep or directly via Azure CLI:
az containerapp update --name staging-env-api --resource-group engram-rg \
--set-env-vars 'CORS_ORIGINS=["https://engram.work","http://localhost:5173"]'
Issue 3: Auth Blocking OPTIONS
Symptom: OPTIONS returns 401 Unauthorized
Cause: Auth middleware runs before CORS handler
Fix: Ensure CORSPreflightMiddleware is added AFTER CORSMiddleware (runs before in execution order)
Issue 4: Azure Platform Auth (Easy Auth) Enabled
Symptom: OPTIONS returns 401 Unauthorized or 302 Redirect before reaching app logs
Cause: Azure Container Apps “Authentication and Authorization” feature intercepts requests.
Fix: Disable Platform Auth via CLI (or ensure Bicep authConfigs resource is correctly applied):
az containerapp auth update --name staging-env-api --resource-group engram-rg \
--enabled false --action AllowAnonymous
Testing
CLI Test
curl -I -X OPTIONS https://api.engram.work/api/v1/chat \
-H "Origin: https://engram.work" \
-H "Access-Control-Request-Method: POST"
Expected headers:
HTTP/2 200
Access-Control-Allow-Origin: https://engram.work
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST
Browser Test
- Open
https://engram.work - DevTools → Network → Filter “OPTIONS”
- Send a chat message
- Verify OPTIONS returns 200 with CORS headers