CORS misconceptions in AI-generated code
Por Seaworthy · 12 de junio de 2026
CORS is one of those browser mechanisms that is just annoying enough to trip over, just opaque enough that most people google the fix, and just well-documented enough that AI models have ingested thousands of Stack Overflow threads about it. The result is a lot of generated code that technically makes the error go away, while creating a problem that won't show up until something goes wrong in production.
Let me be direct about where I see this go sideways most often.
When Access-Control-Allow-Origin: * is actually fine
The wildcard origin is safe for genuinely public resources. If you have a public API that anyone on the internet can call without any kind of session or credential, * is correct. A public weather API, a read-only dataset, a CDN-hosted font: all fine. The browser is enforcing a policy on behalf of the user, not the server. If you want any page on the internet to be able to fetch your resource, then * is exactly what you want.
Where I see AI-generated code get this right by accident: simple hobby APIs, public data endpoints, static file servers. The generated code works because the use case genuinely allows unrestricted access.
Where it becomes a real problem
The issue is context collapse. When you ask an AI assistant to "fix my CORS error" on an endpoint that does require authentication, it will often produce this:
// Express example
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
This is plausible-looking code. It even includes Authorization in the allowed headers, which looks like it's thinking about auth. But it's wrong for any endpoint that uses cookies or sessions, because the browser will refuse to send credentials to a wildcard origin anyway. And for endpoints that use Authorization headers or API keys, the wildcard is just an invitation for any page on the internet to call your API from a victim's browser context.
The model has pattern-matched to "fix CORS" and produced the most common fix it has seen, regardless of whether that fix is appropriate for your situation.
The wildcard-plus-credentials trap
There is one combination that is always wrong, full stop:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
The spec explicitly forbids this. Browsers will reject it. If you try to do this, the preflight will fail with a CORS error even if your server happily sends both headers. Some generated code produces this exact combination when someone asks how to allow cross-origin requests with credentials, and then the developer spends an hour wondering why their fix didn't work.
When you need credentialed cross-origin requests (cookies, HTTP auth), you have to:
- Set
Access-Control-Allow-Originto the specific origin, not* - Set
Access-Control-Allow-Credentials: true - In your fetch call, set
credentials: 'include'
A concrete implementation:
const allowedOrigins = ['https://app.example.com', 'https://staging.example.com'];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.vary('Origin'); // required for correct CDN caching
}
next();
});
Note the Vary: Origin header. Without it, a CDN might cache a response with one origin and serve it to a different one. This is another thing generated code consistently omits.
CORS is not authentication
This is the conceptual mistake underneath most of the bad configs. CORS is a browser-enforced policy. It does not protect your API from non-browser clients. Curl, Postman, a Python script, a server-side attacker: none of them honour CORS headers. They just ignore them.
So Access-Control-Allow-Origin: * on an authenticated endpoint does not mean your data is exposed to the whole internet. It means your data is exposed to any webpage a user happens to visit while logged in to your service. That is a Cross-Site Request Forgery scenario, and it is genuinely bad, but it is a different threat model from "anyone can read this data."
The practical consequence: CORS is not a substitute for authentication. Your API needs auth regardless of your CORS settings. If you remove auth and rely on a restrictive CORS policy, any non-browser client bypasses your security entirely.
How to audit what you have
A quick way to check your current headers against a specific origin:
curl -s -o /dev/null -D - \
-H "Origin: https://evil.example.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS \
https://yourapi.example.com/some-endpoint \
| grep -i "access-control"
If that returns Access-Control-Allow-Origin: * on an endpoint that serves user data, you have a problem to fix. If it returns nothing, you may have a CORS misconfiguration of a different kind (blocking legitimate requests). If it echoes back https://evil.example.com, your allowlist is not working.
For a broader audit, grep your codebase for all places that set CORS headers and check each one against whether the endpoint is public or authenticated. It is a small amount of work and the findings are usually surprising.
The pattern that keeps showing up in AI-assisted codebases is one middleware that sets * for the whole app, added to fix a CORS error in development, and never revisited. The fix worked. The problem it created was invisible until it wasn't.
Seaworthy flags this class of CORS misconfiguration as part of its security checks.
Este artículo fue generado por IA y resume fuentes públicamente disponibles.
Seaworthy analiza tu repositorio en busca de los problemas que se tratan en artículos como este.
Brechas de seguridad, secretos expuestos y configuraciones erróneas detectados antes de desplegar. Gratis, sin cuenta necesaria.