Prompt of the Day: Add CORS Headers That Actually Work
Part 25 of 30 in the Prompt of the Day series.
CORS -- Cross-Origin Resource Sharing -- is the browser security mechanism that decides whether your frontend is allowed to read a response from a different domain, port, or protocol. When it breaks, it breaks loudly: a red console error, a blocked request, and a feature that simply does not work. Fixing it correctly means configuring the server, not the browser, and it means being precise about which origins, methods, and headers you actually intend to allow.
The Prompt
I'm getting a CORS error in my browser console. Here is the exact error message:
[paste error here]
My frontend is running at: [e.g., http://localhost:3000 in dev, https://app.mycompany.com in prod]
My API is running at: [e.g., http://localhost:8000 in dev, https://api.mycompany.com in prod]
My stack is: [e.g., Next.js App Router, Node.js/Express, Supabase Edge Functions]
Please do the following:
1. Diagnose which origin, method, and header is being blocked based on the error message.
2. Add CORS headers to the SERVER (not the frontend) that explicitly allow my frontend origin -- do not use a wildcard * unless this is a fully public, unauthenticated API.
3. Handle the OPTIONS preflight request correctly so the browser does not block it before the real request fires.
4. Configure origins differently for development (localhost) and production (my real domain), using an environment variable or NODE_ENV check so I do not accidentally expose dev origins in production.
5. Show me where in my codebase to place this configuration and confirm I am not setting CORS headers on the client side.
Why It Works
This prompt is effective because it gives the AI three things most CORS prompts omit: the actual error text, both sides of the origin mismatch, and a stated stack. CORS errors are not generic -- the browser console tells you exactly which header is missing and for which origin. Feeding that specificity into the prompt lets the AI skip the guesswork.
The explicit instruction to configure the server, not the frontend, closes the single most common CORS mistake immediately. The request to handle OPTIONS preflight separately matters because many frameworks require explicit handling of that method, and missing it means authenticated or non-simple requests fail even when the origin header looks correct.
Asking for a dev/production split is the difference between a quick fix and a real fix. Leaving localhost in your production allowed-origins list is a quiet security gap that persists long after launch.
The Anti-Prompt
"Just add Access-Control-Allow-Origin: * to fix my CORS error."
This fails in two distinct ways.
First, it is a security risk on any API that handles authenticated requests. The wildcard * is explicitly incompatible with Access-Control-Allow-Credentials: true. If your API uses cookies, session tokens, or Authorization headers, the browser will reject the response regardless of the wildcard -- the spec forbids credentialed requests to wildcard origins. So the "fix" does not even work.
Second, a blanket wildcard tells every browser on the internet that your API is open to requests from any origin. For public read-only data that is sometimes fine. For anything that mutates state, reads user data, or sits behind authentication, it is an open door. As SuperTokens noted in their 2025 CORS guide, production CORS policy should scope allowed origins, methods, and headers to only what the application actually uses.
Variations
Next.js App Router (middleware-based)
Using Next.js 14+ App Router with TypeScript, add CORS configuration in middleware.ts that:
- Reads allowed origins from an ALLOWED_ORIGINS environment variable (comma-separated list)
- Returns a 204 response for OPTIONS preflight requests immediately
- Sets Vary: Origin so CDN caches do not serve the wrong CORS headers to different origins
- Applies only to routes under /api/
Node.js / Express
Using Express with the cors npm package, configure CORS so that:
- Allowed origins are loaded from a list that switches between development and production values based on NODE_ENV
- Preflight OPTIONS requests are handled globally with app.options('*', cors(corsOptions))
- Credentialed requests (cookies + Authorization header) are supported with credentials: true
- The configuration is in a single exported corsOptions object that I can reuse across the app
Supabase Edge Functions
I am writing a Supabase Edge Function in TypeScript (Deno runtime). Add CORS headers so that:
- The OPTIONS preflight request returns immediately with a 200 and the correct headers
- My production frontend domain and localhost:3000 are both allowed origins
- The allowed headers include Authorization and Content-Type
- The same CORS headers are included on every non-OPTIONS response from the function
When You Would Actually Use This
The most common scenario is building a frontend that calls an API on a different subdomain -- app.yourcompany.com talking to api.yourcompany.com. They are different origins even though they share a root domain, so CORS applies. It also comes up constantly during local development when the frontend dev server runs on port 3000 and the API runs on port 8000, and it catches teams off-guard the first time they deploy a Next.js frontend against an external backend they do not control.
As Corsfix documented in their 2025 breakdown of common CORS mistakes, one of the most persistent errors is developers attempting to add Access-Control-Allow-Origin to the outgoing request headers rather than to the server response -- something that has no effect whatsoever. A well-scoped prompt prevents the AI from suggesting exactly that mistake.
Ask The Guild
Have you hit a CORS error that turned out to be caused by something other than a missing header -- a CDN stripping the header, a load balancer blocking OPTIONS, or a backend 500 that prevented the response from sending at all? Share your war story in the comments. The edge cases are where the real learning happens.