Overview

When script on app.example.com fetches api.example.com, the browser sends an Origin header and the server answers with Access-Control-Allow-Origin; on a mismatch the browser hides the response from the script. Configure CORS on the server that returns the data, never on the calling frontend, and list exact origins for any endpoint that uses cookies or tokens. csp is separate: it controls what a page may load, not what it may read.

Rules

  • CORS is not authentication or CSRF protection. A simple request still reaches the server; only the response is withheld.
  • A request is preflighted (an OPTIONS call first) when it uses a method other than GET, HEAD, or POST, a custom header, or a non-safelisted Content-Type such as application/json. The preflight must return 2xx with matching Access-Control-Allow-Methods and -Headers, or the real request is never sent.
  • Access-Control-Allow-Headers: * does not cover Authorization; list it explicitly. Browsers cap Access-Control-Max-Age (2 hours in Chromium, 24 hours in Firefox).
  • With credentials, Access-Control-Allow-Origin: * is rejected. Echo the exact origin from an allowlist, add Access-Control-Allow-Credentials: true, and send Vary: Origin.
  • Same-origin APIs, such as a Next.js route handler called by the same app, need no CORS headers.
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myapp.com"],
    allow_methods=["GET", "POST"],
    allow_headers=["Authorization"],
)