How to vibe safely

How to vibe safely

Neil Millard10 min read

AIvibe coding

Introduction: The Hosting Conundrum

In the grand theatre of web development, hosting is rather like the stage upon which your carefully crafted digital performance unfolds. It's the infrastructure that transforms your brilliant code from a collection of files gathering dust on your local machine into a living, breathing application that users can actually access. One might argue it's the difference between writing a novel and actually publishing it—though admittedly, hosting won't improve your prose.

Hosting, in its essence, is the service that makes your web application accessible to users across the internet. It provides the server infrastructure, network connectivity, and computational resources necessary to serve your application's files and handle user requests. Without proper hosting, your application remains as useful as a chocolate teapot—technically impressive, but utterly impractical.

The importance of hosting extends far beyond mere accessibility. It directly impacts your application's performance, security, scalability, and reliability. A poorly configured hosting environment can transform even the most elegant codebase into a sluggish, vulnerable mess that would make seasoned developers weep into their morning coffee.

Enter Vibe coding—a development paradigm that emphasises rapid prototyping and AI-assisted code generation. While Vibe can produce remarkably functional applications at breakneck speed, it often does so with the security consciousness of a tourist leaving their passport on a café table in Piccadilly Circus. This article explores how to harness Vibe's productivity whilst avoiding the security pitfalls that could turn your deployment into a cautionary tale.

Quick Answer

AI-generated ("vibe coded") apps ship insecure by default in three predictable ways: open databases with no auth — the single most common flaw, so check any generated database config for a default-open network rule before it goes near production — leaked secrets and API keys committed straight into the repo or bundled into client-side code, and missing input validation because the AI optimised for "it works" over "it's safe". Before shipping anything AI-generated: lock down database network access, move every key into environment variables or a secrets manager, and run a dependency/secret scanner over the repo.

Written by [Neil Millard](/about), a cloud and automation specialist with 20+ years' experience delivering infrastructure for organisations including Barclays, HMRC, Marks & Spencer, and AXA.

Understanding Vibe Coding

Vibe coding represents a modern approach to web development that leverages AI-powered tools to generate functional applications rapidly. Unlike traditional development methodologies that emphasise careful planning and incremental building, Vibe coding prioritises speed and iteration, often producing working prototypes in minutes rather than hours.

The philosophy behind Vibe coding is refreshingly straightforward: generate first, refine later. This approach can be remarkably effective for rapid prototyping, proof-of-concept development, and educational purposes. However, it's precisely this "move fast and break things" mentality that creates the security vulnerabilities we'll explore.

The Vibe Advantage

Vibe coding excels in several key areas:

Rapid Prototyping: Complex applications can be generated in minutes, making it ideal for testing concepts and demonstrating functionality to stakeholders who are impressed by anything that loads without a 404 error.

Accessibility: Developers with limited experience in specific technologies can quickly generate functional code, democratising development in a way that would have been unthinkable just a few years ago.

Iteration Speed: The ability to quickly modify and regenerate code allows for rapid experimentation and refinement, though this can lead to a somewhat cavalier attitude towards code quality.

The Security Minefield: Common Vibe Coding Pitfalls

1. Open Databases: The Crown Jewel of Vulnerabilities

The most prevalent security flaw in Vibe-generated code is the creation of databases with overly permissive access controls. In the enthusiasm to create a working application, Vibe tools often generate database configurations that prioritise functionality over security.

Common manifestations include:

// Typical Vibe-generated database configuration
const dbConfig = {
  host: 'localhost',
  port: 5432,
  database: 'myapp',
  username: 'admin',
  password: 'password123',
  ssl: false, // Often disabled for "simplicity"
  allowPublicAccess: true // The kiss of death
};

The production-ready alternative:

// Secure database configuration
const dbConfig = {
  host: process.env.DB_HOST,
  port: process.env.DB_PORT || 5432,
  database: process.env.DB_NAME,
  username: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  ssl: {
    require: true,
    rejectUnauthorized: true
  },
  connectionTimeoutMillis: 30000,
  idleTimeoutMillis: 30000,
  max: 10 // Connection pool limit
};

2. Leaked Secrets and Keys: The Digital Equivalent of Leaving Your House Keys in the Front Door

Vibe coding tools have an unfortunate tendency to hardcode sensitive information directly into source code. This is rather like writing your PIN number on your debit card—convenient, but spectacularly inadvisable.

Common examples of leaked secrets:

// What Vibe generates
const config = {
  apiKey: 'sk-1234567890abcdef',
  secretKey: 'your-secret-key-here',
  databaseUrl: 'postgresql://user:password@localhost:5432/db',
  jwtSecret: 'supersecret123'
};

// Client-side code (visible to everyone)
const stripe = Stripe('pk_live_actual_live_key_why_would_you_do_this');

The secure approach:

// Server-side environment variables
const config = {
  apiKey: process.env.API_KEY,
  secretKey: process.env.SECRET_KEY,
  databaseUrl: process.env.DATABASE_URL,
  jwtSecret: process.env.JWT_SECRET
};

// Client-side with public keys only
const stripe = Stripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);

3. Database Exposure and Unauthorised Access

Vibe-generated applications often expose database endpoints directly to the client-side, creating a delightful buffet of sensitive information for anyone with basic developer tools knowledge.

Problematic pattern:

// Direct database queries from client-side
const getUserData = async (userId) => {
  const query = `SELECT * FROM users WHERE id = ${userId}`;
  return await database.query(query); // SQL injection paradise
};

Secure implementation:

// Server-side API endpoint with proper validation
app.get('/api/user/:id', authenticateToken, async (req, res) => {
  const userId = req.params.id;
  
  // Validate user can access this data
  if (req.user.id !== userId && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Unauthorised' });
  }
  
  // Use parameterised queries
  const query = 'SELECT id, name, email FROM users WHERE id = $1';
  const result = await database.query(query, [userId]);
  
  res.json(result.rows[0]);
});

4. Sensitive Data Leakage

Vibe applications frequently return entire database records to the client, including sensitive fields that should remain server-side only.

Common leakage pattern:

// Returns everything, including sensitive data
const getUser = async (id) => {
  const user = await User.findById(id);
  return user; // Includes password hash, internal IDs, etc.
};

Secure data handling:

// Selective field exposure
const getUser = async (id) => {
  const user = await User.findById(id);
  return {
    id: user.id,
    name: user.name,
    email: user.email,
    avatar: user.avatar,
    createdAt: user.createdAt
    // Notably absent: password, internalId, sensitiveField
  };
};

5. Permission Reconfiguration Vulnerabilities

Vibe tools often generate overly permissive authentication and authorisation systems, creating applications where users can escalate their privileges with minimal effort.

Problematic permission system:

// Client-side role checking (easily bypassed)
const AdminPanel = () => {
  const isAdmin = localStorage.getItem('isAdmin') === 'true';
  
  if (!isAdmin) {
    return <div>Access denied</div>;
  }
  
  return <AdminControls />;
};

Secure permission implementation:

// Server-side middleware for route protection
const requireAdmin = async (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const user = await User.findById(decoded.id);
    
    if (!user || !user.isAdmin) {
      return res.status(403).json({ error: 'Admin access required' });
    }
    
    req.user = user;
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
};

6. Hidden Pages and Admin Interfaces

Perhaps the most amusing security flaw in Vibe applications is the creation of "hidden" admin pages that are about as hidden as a red telephone box. These pages often lack proper authentication and can be accessed by anyone who bothers to type /admin into their browser.

Common hidden page pattern:

// Router configuration
const routes = [
  { path: '/', component: Home },
  { path: '/admin', component: AdminPanel }, // Security through obscurity
  { path: '/debug', component: DebugPanel },
  { path: '/api/internal', component: InternalAPI }
];

Secure routing approach:

// Protected routes with authentication
const ProtectedRoute = ({ component: Component, requiredRole, ...rest }) => {
  const { user, loading } = useAuth();
  
  if (loading) return <Loading />;
  
  if (!user) {
    return <Navigate to="/login" />;
  }
  
  if (requiredRole && !user.roles.includes(requiredRole)) {
    return <Navigate to="/unauthorised" />;
  }
  
  return <Component {...rest} />;
};

// Route configuration
const routes = [
  { path: '/', component: Home },
  { 
    path: '/admin', 
    component: ProtectedRoute,
    props: { component: AdminPanel, requiredRole: 'admin' }
  }
];

Best Practices for Production-Ready Vibe Applications

Environment Configuration

Always separate configuration from code using environment variables:

// .env.production
DATABASE_URL=postgresql://user:pass@prod-db:5432/myapp
JWT_SECRET=super-long-randomly-generated-secret
API_KEY=your-actual-api-key
REDIS_URL=redis://prod-redis:6379

Input Validation and Sanitisation

Implement comprehensive input validation:

const validateUser = (userData) => {
  const schema = Joi.object({
    name: Joi.string().min(2).max(50).required(),
    email: Joi.string().email().required(),
    password: Joi.string().min(8).pattern(new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])')).required()
  });
  
  return schema.validate(userData);
};

Secure Communication

Always use HTTPS in production and implement proper CORS policies:

// CORS configuration
const corsOptions = {
  origin: process.env.ALLOWED_ORIGINS?.split(',') || ['https://yourdomain.com'],
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

Regular Security Audits

Implement automated security scanning in your deployment pipeline:

# GitHub Actions security scan
- name: Run security audit
  run: |
    npm audit --audit-level high
    npm run test:security
    docker run --rm -v "$PWD:/app" securecodewarrior/scanner

Deployment Strategies for Vibe Applications

Containerisation

Package your application in containers for consistent deployment:

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
USER nextjs
EXPOSE 3000
CMD ["npm", "start"]

Infrastructure as Code

Use tools like Terraform or AWS CDK to define your infrastructure:

// AWS CDK example
const vpc = new ec2.Vpc(this, 'VPC', {
  maxAzs: 2,
  natGateways: 1
});

const cluster = new ecs.Cluster(this, 'Cluster', {
  vpc,
  containerInsights: true
});

const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
  memoryLimitMiB: 512,
  cpu: 256
});

Monitoring and Observability

Implement comprehensive monitoring for your production application:

// Application monitoring
const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

// Performance monitoring
const performanceMiddleware = (req, res, next) => {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    logger.info('Request processed', {
      method: req.method,
      url: req.url,
      status: res.statusCode,
      duration: `${duration}ms`
    });
  });
  
  next();
};

Conclusion: Embracing Vibe Whilst Avoiding Disaster

Vibe coding represents a fascinating evolution in web development—a tool that can transform ideas into functional applications with remarkable speed. However, like a sports car with faulty brakes, its power comes with significant risks that require careful management.

The security vulnerabilities inherent in Vibe-generated code are not insurmountable obstacles but rather predictable challenges that can be addressed through systematic application of security best practices. By understanding these common pitfalls—open databases, leaked secrets, permission vulnerabilities, and hidden admin interfaces—developers can proactively implement safeguards that preserve Vibe's productivity benefits whilst ensuring production-ready security.

Key takeaways for production-ready Vibe applications:

  1. Never deploy generated code directly—always review and refactor for security
  2. Implement proper environment variable management from the outset
  3. Separate client-side and server-side concerns rigorously
  4. Apply defence-in-depth principles with multiple layers of security
  5. Automate security scanning in your deployment pipeline
  6. Monitor and log everything in production environments

Next steps for teams adopting Vibe coding:

Establish a security review process that treats Vibe-generated code as a first draft rather than a final product. Develop organisation-specific security templates and guidelines that can be applied consistently across projects. Invest in automated security testing tools that can catch common vulnerabilities before they reach production.

Most importantly, remember that whilst Vibe coding can accelerate development, it cannot replace the fundamental need for security consciousness and careful engineering practices. The goal is not to avoid these powerful tools but to use them wisely, balancing speed with security to create applications that are both functional and trustworthy.

After all, there's little point in building something quickly if you're going to spend the next six months explaining to stakeholders why the entire user database is now available for download on various unsavoury corners of the internet. That's the sort of career-limiting conversation that no amount of development velocity can compensate

Need help with your DevOps setup?

Get personalised advice from Neil Millard — DevOps consultant based in Weston-super-Mare.

© 2026 Delta Famiglia Ltd. All rights reserved.