← All Articles 🔬 Product Features High By Nishant Kumar Verma Updated

Why Vibe-Coded Apps Crash in Production (And How to Catch It Early)

AI tools ship your app but don't keep it running. SSL expiry, missing headers, degrading performance, and unhandled errors - the production failures unique to vibe-coded apps.

SSL-001 SSL-002 HDR-001 WEB-003 WEB-004

Quick Answer

Vibe-coded apps crash in production because AI tools generate application logic but skip infrastructure concerns: SSL certificate renewal, security headers, error handling, retry logic, and memory management. These gaps are invisible during development and only surface under real traffic, real time, and real load. Continuous monitoring - uptime checks, SSL expiry tracking, Lighthouse score history, and security header validation - catches these failures before your users do.

The Production Blind Spot in AI-Generated Code

Your app works perfectly on localhost. Bolt generated a clean Next.js frontend, Cursor helped you wire up the Supabase backend, and the demo looks great. You deploy to Vercel, share the link, and move on to building the next feature.

Three weeks later, your SSL certificate expires and users see a browser security warning. Your Lighthouse performance score dropped from 82 to 47 because a new dependency added 400KB of unminified JavaScript. A third-party API starts rate-limiting your requests because the AI never implemented exponential backoff. None of these problems existed during development. All of them exist in production right now.

Gartner's often-quoted figure for the average cost of network downtime is $5,600 per minute, from a 2014 Gartner blog post that also stresses the real cost varies widely from one business to another. For a solo founder, even 30 minutes of undetected downtime means lost signups, lost revenue, and damage to the trust you spent months building. The gap between "it works locally" and "it runs reliably in production" is where vibe-coded apps consistently fail.

SSL Certificates: The Silent Expiration

Let's Encrypt certificates expire every 90 days. When you deploy to Vercel or Netlify, certificate renewal is handled automatically - until it is not. Custom domains, DNS migrations, and domain transfers can silently break the auto-renewal process. One day the ACME challenge fails, and your users see "Your connection is not private" instead of your landing page.

Expired certificates are a common and avoidable cause of HTTPS outages. For vibe-coded apps on VPS deployments scaffolded by Cursor or Claude Code, the risk is higher. AI tools generate the application server but rarely configure certbot, systemd timers, or reverse proxy TLS termination.

// ❌ BAD - Express server on a VPS with no TLS configuration
// AI generated this, you deployed it, and forgot about certificates
import express from 'express';
const app = express();

app.get('/', (req, res) => res.json({ status: 'ok' }));

// HTTP only - no TLS at all
// Even if you add a cert manually, who renews it in 90 days?
app.listen(3000, () => {
  console.log('Server running on port 3000');
});
// ✅ GOOD - Caddy reverse proxy with automatic certificate management
// Caddyfile - handles issuance AND renewal automatically
// your-domain.com {
//   reverse_proxy localhost:3000
//   # That's it. Caddy provisions Let's Encrypt certs
//   # and renews them before expiry. Zero maintenance.
// }

// Plus: set up external SSL monitoring to catch
// renewal failures BEFORE the cert actually expires.
// Alert at 30 days, 14 days, and 7 days remaining.

The fix is not just configuring auto-renewal. The fix is monitoring that auto-renewal is actually working. A certificate monitoring service that checks expiry dates weekly gives you a 30-day warning window to fix problems before they become outages.

Security Headers Nobody Configured

Security headers are HTTP response headers that tell browsers how to handle your content. They prevent clickjacking, enforce HTTPS, block XSS payloads, and stop MIME-type sniffing. AI coding tools rarely add them.

According to the HTTP Archive's 2024 Web Almanac, only 19% of hosts send a Content-Security-Policy header. Bolt, Lovable, v0, and Cursor generate components, routes, and API handlers - not next.config.js security header blocks or Vercel vercel.json header rules.

Header What It Prevents Set by AI Tools? Set by Vercel/Netlify?
Strict-Transport-Security (HSTS) Protocol downgrade attacks No Yes (Vercel and Netlify both send it by default)
Content-Security-Policy XSS, script injection No No
X-Frame-Options Clickjacking No No
X-Content-Type-Options MIME-type sniffing No No
Referrer-Policy URL data leakage No No
Permissions-Policy Unauthorized browser API access No No

The problem is not that these headers are hard to add - a single configuration block handles all of them. The problem is that nobody checks whether they are present after deployment. Without periodic header validation, your app sits exposed to attacks that a five-minute configuration change would prevent.

Lighthouse Scores That Degrade Over Time

A fresh Bolt or Lovable app might score 75 on Lighthouse performance. Then you add a rich text editor, a charting library, and a few more pages. Three months later the score is 41 and you have no idea when it happened or what caused it.

Google's 2016 mobile speed research found that 53% of mobile site visits are abandoned if a page takes longer than 3 seconds to load. Lighthouse builds its performance score from lab metrics (FCP, Speed Index, LCP, TBT and CLS). Two of them, LCP and CLS, are also Core Web Vitals, which Google says align with what its ranking systems seek to reward. A degrading score means degrading SEO, degrading conversion rates, and degrading user experience - all happening gradually enough that you do not notice without trend data.

AI-generated code is particularly prone to performance regression. Every time you prompt Cursor or Bolt to add a feature, it may pull in a new npm package. The HTTP Archive's 2024 Web Almanac reports that the median mobile page now weighs about 2.3MB, with about 570KB of JavaScript. Vibe-coded apps frequently exceed both numbers because AI tools optimize for functionality, not bundle size. A single import dayjs from 'dayjs' when you already have date-fns installed adds redundant weight that only shows up in Lighthouse.

// ❌ BAD - AI keeps adding full library imports
// Each prompt adds another heavy dependency
import moment from 'moment';        // 60KB+ minified
import lodash from 'lodash';        // 71KB minified
import Chart from 'chart.js/auto';  // 200KB+ minified

// Three prompts, three libraries, 300KB+ added to client bundle
// and the Lighthouse performance score drops
// ✅ GOOD - Tree-shakeable imports + monitoring
import { formatDistance } from 'date-fns';  // bundles only what it needs
import groupBy from 'lodash/groupBy';       // ~22KB instead of ~72KB
import { Line } from 'react-chartjs-2';     // Only the Line chart

// Track your Lighthouse score after every deploy.
// If the score drops more than 5 points, investigate
// what changed in the most recent push.

Without historical Lighthouse data, you cannot answer the question "when did my site get slow?" With weekly score tracking, you can pinpoint the exact deploy that caused the regression and revert or fix it.

Unhandled Errors and Memory Leaks

AI-generated code has a pattern: it handles the happy path beautifully and ignores everything else. Unhandled promise rejections, uncaught exceptions, and missing error boundaries are common runtime failure modes in vibe-coded apps.

Even a site with 99.95% availability is down for more than 4 hours a year. For apps without error handling or monitoring, that number is significantly worse. A single unhandled exception in a Node.js process crashes the entire server unless you have a process manager configured to restart it.

// ❌ BAD - AI-generated API route with no error handling
// In Express 4, any database error here is an unhandled rejection
// that crashes the Node.js process (Express 5 passes it to next())
app.get('/api/users/:id', async (req, res) => {
  const user = await db.query(`SELECT * FROM users WHERE id = $1`, [req.params.id]);
  const orders = await fetch(`https://api.stripe.com/v1/charges?customer=${user.stripeId}`, {
    headers: { Authorization: `Bearer ${process.env.STRIPE_KEY}` }
  });
  // No try/catch - database timeout = server crash
  // No retry logic - Stripe rate limit = unhandled rejection
  // No timeout - slow API = request hangs forever
  res.json({ user: user.rows[0], orders: await orders.json() });
});
// ✅ GOOD - Proper error handling with retry and timeout
app.get('/api/users/:id', async (req, res) => {
  try {
    const user = await db.query(`SELECT * FROM users WHERE id = $1`, [req.params.id]);
    if (!user.rows[0]) return res.status(404).json({ error: 'User not found' });

    const orders = await fetchWithRetry(
      `https://api.stripe.com/v1/charges?customer=${user.rows[0].stripeId}`,
      { headers: { Authorization: `Bearer ${process.env.STRIPE_KEY}` },
        timeout: 5000, retries: 3, backoff: 'exponential' }
    );
    res.json({ user: user.rows[0], orders });
  } catch (err) {
    console.error('Failed to fetch user data:', err.message);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Memory leaks follow a similar pattern. AI tools add event listeners inside React useEffect hooks without cleanup functions, so a new listener piles up every time the effect runs again, and a long session in the browser gets slower until the tab freezes. On the server, a cache or listener that grows with every request does the same to your Node.js process until it runs out of memory and crashes. Uptime monitoring catches that crash. Without it, the site is down until someone reports it.

Our guide to memory leaks in useEffect shows the cleanup pattern for intervals and event listeners, and how React Strict Mode exposes a missing one.

How to Set Up Continuous Monitoring

The solution is not fixing each problem individually - it is setting up a monitoring baseline that catches all of them automatically. Here is what a complete monitoring stack looks like for a vibe-coded app:

Uptime monitoring checks whether your site responds to HTTP requests at regular intervals (every 1-5 minutes). When the response fails or the status code changes, you get alerted. This catches server crashes, deployment failures, DNS issues, and hosting outages.

SSL certificate monitoring tracks your certificate's expiry date and warns you 30, 14, and 7 days before it expires. This catches broken auto-renewal before it becomes an outage.

Lighthouse score tracking runs performance audits on a schedule and stores the results over time. When your score drops, you can correlate it with recent deploys to find the cause.

Security header validation checks that your response headers include HSTS, CSP, X-Frame-Options, and other protections. If a deployment accidentally removes a header, you find out immediately instead of months later during a security audit.

Tools like VibeDoctor's Vibe Monitoring combine uptime checks (with uptime percentage, response time and days left on your SSL certificate) with a Lighthouse audit and a security header check on every URL scan, in one dashboard - built specifically for vibe-coded apps that need continuous oversight. Free to sign up.

The key principle is that monitoring is not optional for AI-generated apps. Human developers build operational awareness over time - they know which parts of the infrastructure are fragile. Vibe coders skip that entire learning process. Monitoring fills the gap.

Some outages are not in your code at all. On 20 October 2026 a power failure took one Google Cloud zone in London offline, and apps that ran only in that zone were unreachable. Here is what failed and what to change.

A Production Monitoring Checklist

If you shipped a vibe-coded app and it is running in production right now, here is what to verify:

  1. Uptime check active? Something should ping your URL every 1-5 minutes and alert you on failure. If you do not have this, you are relying on user reports to learn about downtime.
  2. SSL expiry known? Check when your certificate expires. If you cannot answer this in 10 seconds, you need SSL monitoring.
  3. Security headers present? Open your browser DevTools, go to Network, click on your main document request, and check the Response Headers. Look for Strict-Transport-Security, Content-Security-Policy, and X-Frame-Options. If they are missing, add them.
  4. Lighthouse baseline recorded? Run a Lighthouse audit now and save the score. Run it again next month. If the score dropped, find out why.
  5. Error handling in place? Search your codebase for async functions without try/catch blocks. Every API route and database query needs error handling.
  6. Process manager configured? If you are on a VPS, verify that PM2, systemd, or Docker restart policies will restart your app after a crash.

Each item on this list represents a failure mode that has taken down a production vibe-coded app. The founders who handle them proactively sleep better. The ones who do not find out about problems from angry users on Twitter.

FAQ

Why do vibe-coded apps fail in production more than hand-written apps?

AI tools optimize for getting code to work, not for keeping it running. They skip infrastructure concerns like certificate management, error recovery, retry logic, and performance budgets. A human developer learns these lessons from past outages. AI tools repeat the same gaps in every project because they generate code without operational context.

Is Vercel or Netlify monitoring enough for my app?

Vercel and Netlify provide deployment status, logs and analytics, and Vercel's Speed Insights tracks real-user performance over time. But they do not check your security headers for you, and a successful deployment does not tell you whether the deployed app stays up and healthy. You need separate monitoring for the live site experience.

How often should I check my Lighthouse performance score?

At minimum, after every significant deploy. Ideally, on a weekly automated schedule so you catch gradual regressions from dependency creep and content additions. A single score is a snapshot. A trend line over weeks tells you whether your app is getting faster or slower.

Can AI tools fix the monitoring gaps they create?

Partially. You can prompt Cursor or Claude Code to add error handling, security headers, or retry logic. But the AI will not remember to do it proactively - you have to ask. And it cannot monitor your production environment. Monitoring is an external process that watches your live app, not a code change inside it.

What is the minimum monitoring setup for a solo founder?

At minimum: uptime checks every 5 minutes with email alerts, SSL certificate expiry tracking, and a monthly Lighthouse audit. This covers three common production failures in vibe-coded apps (downtime, expired certificates, and performance regression) with less than 10 minutes of setup time.

Diagnose your codebase - free

VibeDoctor checks for SSL-001, SSL-002, HDR-001, WEB-003, WEB-004 and 148 other issues across 21 diagnostic areas - security, performance, code quality, and more.

SCAN MY APP →