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.
According to Gartner's 2024 infrastructure monitoring research, unplanned downtime costs small businesses an average of $5,600 per minute. 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.
The SSL Store's research found that expired certificates account for 35% of all TLS-related incidents. 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 never add them.
According to the Mozilla Observatory project, less than 30% of websites implement a Content-Security-Policy header. Among freshly deployed vibe-coded apps, the percentage is close to zero. 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 | Partial (Vercel adds it, Netlify does not by default) |
| Content-Security-Policy | XSS, script injection | No | No |
| X-Frame-Options | Clickjacking | No | No |
| X-Content-Type-Options | MIME-type sniffing | No | Vercel adds it |
| 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 Web Vitals research shows that 53% of mobile site visits are abandoned if a page takes longer than 3 seconds to load. Lighthouse performance scores directly correlate with Core Web Vitals (LCP, FCP, CLS, TBT), which Google uses as ranking signals. 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 page now transfers 2.2MB of resources, with JavaScript accounting for 500KB. 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'; // 72KB minified
import lodash from 'lodash'; // 71KB minified
import Chart from 'chart.js/auto'; // 200KB+ minified
// Three prompts, three libraries, 340KB+ added to client bundle
// Lighthouse performance score drops 15-20 points
// ✅ GOOD - Tree-shakeable imports + monitoring
import { formatDistance } from 'date-fns'; // ~2KB
import groupBy from 'lodash/groupBy'; // ~1KB
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 the top runtime failure modes in vibe-coded apps.
Pingdom's 2024 State of the Internet report found that website availability averaged 99.95%, meaning the average site experiences over 4 hours of downtime per 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
// This crashes the entire Node.js process on any database error
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. The listener accumulates on every re-render. After a few thousand page views, the server or client-side JavaScript runs out of memory and crashes silently. Uptime monitoring catches the crash. Without it, the site is down until someone reports it.
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 (vibedoctor.io) combine uptime monitoring, SSL certificate tracking, Lighthouse score history, and security header checks 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.
A Production Monitoring Checklist
If you shipped a vibe-coded app and it is running in production right now, here is what to verify:
- 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.
- SSL expiry known? Check when your certificate expires. If you cannot answer this in 10 seconds, you need SSL monitoring.
- 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.
- Lighthouse baseline recorded? Run a Lighthouse audit now and save the score. Run it again next month. If the score dropped, find out why.
- Error handling in place? Search your codebase for
asyncfunctions withouttry/catchblocks. Every API route and database query needs error handling. - 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 and basic analytics, but they do not monitor SSL expiry, security headers, Lighthouse score trends, or runtime errors on your live site. They tell you whether the deployment succeeded, not whether the deployed app is 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 the three most common production failures in vibe-coded apps (downtime, expired certificates, and performance regression) with less than 10 minutes of setup time.