Building a chat UI that calls your AI API directly from the browser? CORS will block you by default. Here’s a safe, production-ready setup that enables cross-origin requests without opening security holes.
What CORS is (and why your chat breaks)
CORS (Cross-Origin Resource Sharing) is the browser’s safety rail that prevents a page on one domain from freely calling another. Your API must explicitly allow the front-end’s origin with the right headers.
For a deep primer, see MDN’s CORS guide. This piece distills practical guidance inspired by Simon Willison’s notes on CORS for chat.
The safe way to enable CORS for AI chat UIs
- Allow specific origins only: no * for Access-Control-Allow-Origin. Return the exact origin (e.g., https://chat.example.com) you trust.
- Include Vary: Origin so CDNs/proxies cache separate responses per origin.
- Support preflight: Respond to OPTIONS with Access-Control-Allow-Methods: GET, POST, OPTIONS and Access-Control-Allow-Headers: Authorization, Content-Type.
- Avoid cookies: Prefer bearer tokens in the Authorization header. If you must use cookies, set Access-Control-Allow-Credentials: true and never use a wildcard origin.
- Set tight cache and content types: For streaming responses, use the correct Content-Type and disable caches where appropriate.
Streaming and preflight gotchas
- Server-Sent Events (SSE): Return Access-Control-Allow-Origin on the initial response with Content-Type: text/event-stream and Cache-Control: no-cache.
- Fetch streaming: Keep the connection alive and ensure the CORS headers are present on the main response (preflight will still be a separate OPTIONS).
- Custom headers trigger preflight: Authorization or non-simple Content-Type will cause an OPTIONS request—make sure your gateway/app handles it.
- CDN/proxy quirks: Verify they forward the Origin header and don’t strip CORS headers on 4xx/5xx responses (browsers still enforce CORS on errors).
Security pitfalls to avoid
- Never use Access-Control-Allow-Origin: * with credentials. Attackers can exfiltrate user data from your API.
- Issue short-lived, scoped tokens to the browser. Rotate frequently and limit permissions (model, rate, org/account).
- Enforce rate limits per token, IP, and origin. Log the Origin and User-Agent to aid abuse detection.
- Disallow sensitive endpoints from browser use. Keep admin or high-cost routes server-only.
- Validate origins server-side against an allowlist, not just reflect anything you receive.
Minimal header set (typical)
- Access-Control-Allow-Origin: https://chat.example.com
- Vary: Origin
- Access-Control-Allow-Methods: GET, POST, OPTIONS
- Access-Control-Allow-Headers: Authorization, Content-Type
- Access-Control-Max-Age: 600 (cache preflight for 10 minutes)
Deployment checklist
- Verify preflight: OPTIONS returns 204/200 with the right CORS headers.
- Test streaming UI flows (SSE or fetch) in Chrome, Firefox, and Safari.
- Confirm headers on non-2xx responses (CORS must still be present).
- Lock origins to production/staging domains; block localhost in prod.
- Instrument and alert on Origin anomalies and burst traffic.
Takeaway: You can safely run a browser-based AI chat against your API by strictly allowlisting origins, handling preflights, and avoiding credentialed wildcards.
Get more practical AI build notes in your inbox—subscribe to The AI Nuggets newsletter.

