Why AI Generates CORS `origin: "*"` and Is It Safe for Production?
I found CORS wildcard configurations in almost every AI-generated Express or Fastify backend I’ve reviewed. The pattern is consistent: cors({ origin: "*" }) sitting in production code, quietly creating security vulnerabilities that rarely cause visible problems until someone exploits them.
The Problem: CORS Wildcards in AI-Generated Code
Ask any AI assistant to create a backend API, and you’ll likely get something like this:
const express = require('express');const cors = require('cors');
const app = express();
// This works in development but is dangerous in productionapp.use(cors({ origin: "*" }));
app.get('/api/users', (req, res) => { res.json({ users: ['alice', 'bob'] });});
app.listen(3000);This configuration “just works” when your frontend runs on port 5173 and your backend on port 3000. No CORS errors. No debugging. The AI solved the immediate problem and moved on.
Why AI Defaults to Permissive CORS
AI code assistants prioritize getting you unstuck. When you ask for a backend that “works,” the AI optimizes for:
- Development friction reduction - Wildcard CORS eliminates the most common development error
- Minimal context requirements - No need to ask about your frontend URL
- Immediate success signal - The code runs without errors on first try
The AI doesn’t know you’re building for production. It doesn’t know your security requirements. It picks the path of least resistance.
A Reddit developer put it well: “Almost every AI-generated Express/Fastify backend I’ve scanned has cors({ origin: "*" }) or cors({ origin: true }). I think a lot of people don’t even realize it’s still in there because it never causes a visible problem.”
What CORS Origin Wildcard Actually Does
CORS (Cross-Origin Resource Sharing) is a browser security mechanism. When your frontend at https://app.example.com makes a request to your API at https://api.example.com, the browser checks if the API allows cross-origin requests.
With origin: "*", your API tells browsers: “Any website can read responses from this endpoint.”
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, PUT, DELETEAccess-Control-Allow-Headers: Content-TypeThis means https://malicious-site.com can make requests to your API and read the responses through the victim’s browser.
Security Risks in Production
Data Exfiltration
If your API returns sensitive user data, any website can access it:
// This runs on https://evil.comfetch('https://your-api.example.com/api/user/profile', { credentials: 'include' // Includes cookies for authenticated requests}).then(res => res.json()).then(data => { // Attacker now has your user's profile data sendToAttackerServer(data);});When a logged-in user visits the attacker’s site while your origin: "*" is active, their browser happily sends the request with their session cookie, and the response is readable by the attacker’s JavaScript.
CSRF Amplification
While CORS doesn’t enable CSRF directly (CSRF works without reading responses), wildcard CORS makes certain attacks more dangerous:
// Attacker can now read the response to verify attack successfetch('https://your-api.example.com/api/account/delete', { method: 'POST', credentials: 'include', body: JSON.stringify({ confirm: true })}).then(res => res.json()).then(result => { if (result.success) { // Attacker confirms the deletion worked notifyAttacker(); }});Credential Exposure with Wildcards
A critical mistake I’ve seen: combining credentials with wildcards:
// This is a SECURITY ERROR in most browsersapp.use(cors({ origin: "*", credentials: true // This combination is rejected by browsers}));Browsers reject Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true. But the HTTP response still contains your sensitive data - the browser just blocks JavaScript access. Network-level attackers (or certain exploits) can still read it.
When Wildcard Is Acceptable vs Never Safe
Wildcard IS Acceptable For:
- Public APIs with no authentication and intentionally public data
- Development environments (never deploy to production)
- Static asset CDNs serving images, fonts, or public files
// Acceptable for truly public, read-only dataapp.get('/api/public/health', cors({ origin: "*" }), (req, res) => { res.json({ status: 'ok', timestamp: Date.now() });});Wildcard Is NEVER Safe For:
- Authenticated endpoints - Any route requiring session/cookie/JWT
- User-specific data - Profile info, private messages, account settings
- Write operations - POST, PUT, DELETE endpoints
- Internal APIs - Services not meant for public consumption
- Any production deployment without explicit security review
Secure Configuration Options
Static Whitelist
The simplest approach for known frontend origins:
const cors = require('cors');
const allowedOrigins = [ 'https://app.example.com', 'https://staging.example.com', 'http://localhost:5173' // Development only];
app.use(cors({ origin: allowedOrigins, credentials: true}));Dynamic Validation
For multi-tenant or dynamic subdomain scenarios:
const cors = require('cors');
app.use(cors({ origin: (origin, callback) => { // Allow requests with no origin (mobile apps, curl) if (!origin) return callback(null, true);
// Check against allowed patterns const allowedPatterns = [ /^https:\/\/[a-z-]+\.example\.com$/, // *.example.com /^http:\/\/localhost:\d+$/ // localhost any port (dev only) ];
const isAllowed = allowedPatterns.some(pattern => pattern.test(origin));
if (isAllowed) { callback(null, true); } else { callback(new Error('CORS policy violation')); } }, credentials: true}));Environment-Based Configuration
Keep development flexibility while enforcing production security:
const cors = require('cors');
const corsOptions = { credentials: true, origin: process.env.NODE_ENV === 'production' ? process.env.ALLOWED_ORIGINS.split(',') : true // Allow all in development};
app.use(cors(corsOptions));NODE_ENV=productionALLOWED_ORIGINS=https://app.example.com,https://admin.example.comFastify Equivalent
const fastify = require('fastify')();
await fastify.register(require('@fastify/cors'), { origin: process.env.NODE_ENV === 'production' ? process.env.ALLOWED_ORIGINS.split(',') : true, credentials: true});Production Security Checklist
Before deploying any API with CORS:
- Audit all AI-generated CORS configurations
- Replace
origin: "*"with explicit origin list - Verify
credentials: trueonly used with specific origins (never wildcards) - Test from an unauthorized origin (response should be blocked)
- Review cookie settings:
SameSite,Secure,HttpOnlyflags - Add CORS-related security headers (see below)
- Remove development origins from production whitelist
app.use((req, res, next) => { // Additional security headers res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('X-XSS-Protection', '1; mode=block');
// Strict CSP for API responses res.setHeader('Content-Security-Policy', "default-src 'none'");
next();});Summary
AI-generated origin: "*" works in development but creates production vulnerabilities. The wildcard exposes your API to:
- Cross-site data exfiltration - Any website can read your API responses
- Amplified CSRF attacks - Attackers can verify attack success
- Compliance violations - GDPR, HIPAA, SOC2 require access controls
The fix is straightforward: replace wildcards with explicit origin whitelists. Use environment variables to maintain development flexibility while enforcing production security. Review any AI-generated backend code before it reaches production - the CORS misconfiguration might be invisible until it’s exploited.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 MDN Cross-Origin Resource Sharing (CORS)
- 👨💻 OWASP CORS Security Cheat Sheet
- 👨💻 Reddit Discussion: Vibe-coded CORS issues
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments