April 11, 2026

Why Cursor Keeps Generating Wildcard CORS -- And How to Fix It

TL;DR AI editors almost always default to cors() with no config -- which sets...

Why Cursor Keeps Generating Wildcard CORS -- And How to Fix It

TL;DR

  • AI editors almost always default to cors() with no config -- which sets Access-Control-Allow-Origin: *
  • Wildcard CORS on authenticated APIs exposes your users to cross-site request attacks
  • Fix: replace the wildcard with an explicit origin allowlist controlled by an env var

I was reviewing a side project a dev built entirely in Cursor. The Express backend looked clean -- structured routes, solid error handling, decent auth middleware. Then I checked the CORS setup.

app.use(cors()); // defaults to Access-Control-Allow-Origin: *
Enter fullscreen mode Exit fullscreen mode

One line. Auto-suggested by Cursor from a starter template. Left in production because it made the frontend stop complaining in dev. Except this was prod.

Wildcard CORS feels harmless compared to SQL injection. No immediate data breach. But for any API using cookies or session tokens, a wildcard CORS config means any website -- a phishing page, a malicious ad iframe -- can make authenticated requests on behalf of your logged-in users without them knowing.

The vulnerable pattern (CWE-942)

Here's what Cursor and Claude Code almost always suggest when you ask for a working Express backend:

// CWE-942: Permissive Cross-domain Policy
const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors()); // Allows all origins -- no questions asked
app.use(express.json());

app.get('/api/user/profile', authenticate, (req, res) => {
  res.json(req.user);
});
Enter fullscreen mode Exit fullscreen mode

The cors() call with no arguments sets Access-Control-Allow-Origin: * on every response. If your frontend then adds credentials: true to fetch calls, modern browsers will block the request and throw a CORS error. At that point developers often "fix" it like this:

// Even worse
app.use(cors({ origin: '*', credentials: true }));
Enter fullscreen mode Exit fullscreen mode

Browsers block this combination too (you cannot use wildcard origin with credentials). But the intent matters: developers are being guided step by step toward maximally permissive configs, just to silence console errors.

Why AI editors keep generating this

Training data skews toward "this works" not "this is safe." Hundreds of tutorials, Stack Overflow answers, and README files show app.use(cors()) as the one-liner to get your frontend talking to your backend. That's what the model learned. When Cursor generates a backend starter, it's optimizing for immediate functionality -- wildcard CORS delivers that, so it gets generated.

The security implications don't surface until much later, if at all. Most projects never get a security review.

The actual fix

Replace the wildcard with an explicit origin allowlist. Keep the list in environment variables so staging and production configs stay separate:

// Explicit origin allowlist
const allowedOrigins = process.env.ALLOWED_ORIGINS
  ? process.env.ALLOWED_ORIGINS.split(',')
  : [];

app.use(cors({
  origin: (origin, callback) => {
    // Allow server-to-server requests (no Origin header)
    if (!origin) return callback(null, true);
    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error(`CORS blocked: ${origin}`));
    }
  },
  credentials: true // safe -- origin is explicitly verified before this applies
}));
Enter fullscreen mode Exit fullscreen mode

Your .env:

ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
Enter fullscreen mode Exit fullscreen mode

If you're not using the cors library:

// Manual headers -- explicit and auditable
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.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  if (req.method === 'OPTIONS') return res.sendStatus(204);
  next();
});
Enter fullscreen mode Exit fullscreen mode

The critical difference: you're echoing back the verified origin, not broadcasting a wildcard. Every CORS request gets an explicit answer.

One more thing -- add this to your semgrep config to catch wildcard CORS before it reaches code review:

rules:
  - id: cors-wildcard
    patterns:
      - pattern: cors({ ..., origin: '*', ... })
      - pattern: cors()
    message: Wildcard CORS detected -- replace with explicit allowlist
    severity: WARNING
    languages: [javascript, typescript]
Enter fullscreen mode Exit fullscreen mode

I've been running SafeWeave for this. It hooks into Cursor and Claude Code as an MCP server and flags these patterns before I move on. That said, even a basic pre-commit hook with semgrep will catch most wildcard CORS configs in seconds. The important thing is catching it early, whatever tool you use.

Ready to build your next web project? Let's work together.