Skip to main content
Back to blog
May 12, 2026|6 min read|Antoine Duno|Web Security

The 7 Most Common Security Mistakes in Vibe-Coded Apps (2026)

AI coding tools make you 10x faster. They also make the same 7 security mistakes on every project. Here's what they are, why they happen, and how to fix them before you ship.

Antoine Duno

936 words

AD

Antoine Duno

Founder of ZeriFlow · 10 years fullstack engineering · About the author

Key Takeaways

  • AI coding tools make you 10x faster. They also make the same 7 security mistakes on every project. Here's what they are, why they happen, and how to fix them before you ship.
  • Includes copy-paste code examples and step-by-step instructions.
  • Free automated scan available to verify your implementation.

AI coding tools have a pattern. They generate clean, readable, functional code. They handle the feature you described. They connect the UI to the API to the database. And they make the same seven security mistakes on virtually every project.

This is not a criticism of the tools — it is a structural reality. Large language models are trained to generate code that works. Security configuration lives at the intersection of code, deployment, and infrastructure in ways that are genuinely difficult to encode in training data.

Here are the seven mistakes, why they happen, and how to fix each one.

Is your site actually secure?

Run a free check — 60 seconds

Scan free →

Mistake 1: No Security Headers (73% of Apps)

What it looks like: Your app ships with no X-Frame-Options, no Content-Security-Policy, no Strict-Transport-Security, no X-Content-Type-Options.

Why AI tools make this mistake: Security headers are a deployment configuration concern, not a feature concern. When you ask an AI to "build a user authentication system," you get the auth code. The headers that go on the HTTP responses are outside the scope of what was requested.

Why it matters: Missing X-Frame-Options enables clickjacking. Missing CSP increases XSS risk. Missing HSTS means HTTPS can be downgraded.

The fix for Next.js:

javascript
// next.config.js
const securityHeaders = [
  { key: 'X-Frame-Options', value: 'DENY' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
]

module.exports = {
  async headers() {
    return [{ source: '/(.*)', headers: securityHeaders }]
  }
}

Mistake 2: Wildcard CORS

What it looks like:

javascript
app.use(cors({ origin: '*' }))

Why AI tools make this mistake: Almost every CORS tutorial starts with origin: '*' as the example. It is the path of least resistance and makes the code work immediately. LLMs learn from this pattern heavily.

Why it matters: Wildcard CORS means any website can make cross-origin requests to your API. For APIs handling session cookies or sensitive user data, this creates real attack surface.

The fix:

javascript
app.use(cors({
  origin: process.env.NODE_ENV === 'production'
    ? 'https://yourdomain.com'
    : 'http://localhost:3000',
  credentials: true,
}))

Mistake 3: Example API Keys Left in Code

Why AI tools make this mistake: LLMs use realistic-looking placeholder values so the code structure is clear. The placeholder looks enough like a real key that developers sometimes commit the real value in the same spot.

Why it matters: Hardcoded credentials get committed to git, potentially pushed to public repositories, and exposed in build artifacts.

The fix:

bash
# Find potential secrets before every commit
git grep -iE "(api_key|secret|password|token)"

# Use environment variables for everything
const apiKey = process.env.OPENAI_API_KEY

Mistake 4: API Routes Without Authentication

Why AI tools make this mistake: LLMs generate the happy path. When asked to "create an API endpoint that returns user data," they write exactly that — without the authentication guard.

Why it matters: An unauthenticated API route that returns user data is a data breach waiting to happen.

The fix:

javascript
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'

export async function GET(request) {
  const session = await getServerSession(authOptions)
  if (!session?.user) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }
  // proceed only if authenticated
}

Mistake 5: Cookies Without Secure/HttpOnly Flags

Why AI tools make this mistake: Default cookie configurations in most frameworks do not set security flags. LLMs generate code using the defaults.

Why it matters: A session cookie without HttpOnly can be stolen via XSS. A cookie without Secure can be transmitted over plain HTTP.

The fix:

javascript
res.cookie('session', token, {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'lax',
  maxAge: 7 * 24 * 60 * 60 * 1000
})

Mistake 6: Missing Rate Limiting on Auth

Why AI tools make this mistake: Rate limiting requires additional infrastructure (Redis, middleware) that goes beyond the scope of what was prompted.

Why it matters: Without rate limiting, your login endpoint can be hit millions of times per hour with credential stuffing attacks.

The fix with Upstash:

javascript
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '15 m'),
})

export async function POST(request) {
  const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1'
  const { success } = await ratelimit.limit(ip)
  if (!success) {
    return Response.json({ error: 'Too many attempts' }, { status: 429 })
  }
}

Mistake 7: Verbose Error Messages in Production

Why AI tools make this mistake: During development, verbose errors are helpful. LLMs optimize for debuggability. The distinction between development and production error handling requires explicit prompting.

Why it matters: Stack traces expose file paths, library versions, and code structure to attackers.

The fix:

javascript
try {
  // some operation
} catch (error) {
  console.error('Internal error:', error)  // log server-side only
  return Response.json(
    { error: 'An unexpected error occurred' },
    { status: 500 }
  )
}

How to Catch All 7 in 60 Seconds

ZeriFlow automatically detects mistakes 1 (security headers), 2 (wildcard CORS), and 5 (cookie flags) in a 60-second scan of your deployed URL, plus partially surfaces issues related to exposed endpoints and authentication gaps on public routes.

Run a free scan at zeriflow.com/free-scan before every production deployment. The combination of automated scanning plus the manual checklist above covers the full attack surface that vibe-coded apps typically leave open.

Scan your vibe-coded app before you ship — free.

80+ checks in 60 seconds. Find every security gap AI tools left behind.

Related articles

Keep reading