TL;DR
Client-side = runs in the browser. Anyone can see it. Server-side = runs on your server. Only you can see it. If your AI puts API keys, user data, or business logic in the browser code, every visitor to your site can read it. The fix is knowing which code belongs where — and telling your AI explicitly.
Why AI Coders Need to Know This
Here is the deal. When you ask Claude, ChatGPT, or Cursor to "build me a waitlist page," the AI is optimizing for one thing: making it work. It is not thinking about who can see the code. It is not thinking about whether your Supabase key should be visible to the public. It is solving the shortest path to a functioning feature.
That means the AI will happily put your database credentials in a JavaScript file that gets shipped to every browser that loads your page. It will embed your entire user list in a React component. It will hardcode your Stripe secret key in a frontend fetch call. Not because it is trying to sabotage you — because you did not tell it where the code should run.
This is the single biggest security mistake vibe coders make. And it is happening constantly because people are building real products with AI tools without understanding the one concept that separates "it works on my screen" from "it works safely in production."
Client-side vs server-side is not some academic theory. It is the difference between a launched product and a data breach.
Think of It Like a House
If you have been in construction, this will click immediately.
Client-side is the front of the house. It is everything the visitor can see when they walk up: the siding, the windows, the front door, the landscaping. Anyone driving by can look at it. Anyone walking up can touch it. You cannot hide anything on the front of the house. If you tape your house key to the front door, everyone in the neighborhood can see it.
Server-side is the mechanical room. It is behind a locked door in the basement. The electrical panel, the plumbing manifold, the HVAC system, the security system brain. Visitors never see it. They interact with the results — they flip a light switch and the light comes on — but they do not have access to the wiring, the breaker labels, or the alarm codes.
When someone says "keep that server-side," they mean put it in the mechanical room. Lock it up. Visitors get results, not access.
When your AI puts an API key in the frontend code, it is literally taping the alarm code to the front door. It works — the alarm system functions — but now every visitor knows how to disable it.
What Client-Side Actually Means
Client-side code is any code that runs in the user's browser. When someone visits your website, their browser downloads your HTML, your CSS, and your JavaScript. Then the browser runs that code locally on the visitor's computer.
Here is what that means in practice:
- Every line of your frontend JavaScript is visible. Right-click → Inspect → Sources tab. It is all there. Minified code? A click on "Pretty Print" makes it readable. Bundled with Webpack? The source maps often ship too.
- Every API call the browser makes is visible. Open the Network tab and you can see every URL, every request body, every header, and every response. If the browser fetches your full user list, the visitor can read every record.
- Every variable, every string, every key in your JavaScript is visible. If your code says
const API_KEY = "sk-abc123...", anyone can find it.
Client-side code handles things the user needs to interact with directly: displaying content, handling button clicks, form validation (the initial check, not the security check), animations, navigation, and making requests to your server for data.
Here is an example of client-side code doing something appropriate — showing and hiding a mobile menu:
// This is fine as client-side code. No secrets here.
const menuButton = document.querySelector('.menu-toggle');
const mobileNav = document.querySelector('.mobile-nav');
menuButton.addEventListener('click', () => {
mobileNav.classList.toggle('open');
const isOpen = mobileNav.classList.contains('open');
menuButton.setAttribute('aria-expanded', isOpen);
});
That code is safe in the browser. It is just toggling a CSS class. There is nothing secret about opening a menu.
What Server-Side Actually Means
Server-side code runs on a computer you control — a server, a cloud function, a VPS — somewhere the visitor's browser never touches. The user's browser sends a request. Your server processes it, does the sensitive work, and sends back only the result.
The visitor never sees the server code. They cannot right-click and inspect it. They cannot open DevTools and read your database queries. They only see what you choose to send back.
Server-side code handles things that need to stay private:
- Database queries. Reading and writing user data, checking if an email exists, storing orders.
- API keys and secrets. Your Stripe key, your Supabase service role key, your OpenAI key, your email service credentials. These live in environment variables on the server.
- Business logic. Pricing calculations, discount validation, subscription checks. If you calculate prices in the browser, someone can change them.
- Authentication checks. Verifying passwords, checking tokens, confirming identity. The browser sends credentials; the server validates them.
Here is what a server-side API endpoint looks like. This is the code that would run on your server when the browser requests the waitlist:
// This runs on the SERVER. Visitors never see this code.
// File: /api/waitlist.js (server-side)
import { createClient } from '@supabase/supabase-js';
// These keys come from environment variables on the server.
// They are NEVER sent to the browser.
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_KEY
);
export default async function handler(req, res) {
if (req.method === 'POST') {
const { email } = req.body;
// Server validates the email
if (!email || !email.includes('@')) {
return res.status(400).json({ error: 'Valid email required' });
}
// Server talks to the database directly
const { error } = await supabase
.from('waitlist')
.insert({ email, signed_up_at: new Date() });
if (error) {
console.error('Waitlist insert failed:', error);
return res.status(500).json({ error: 'Something went wrong' });
}
// Only the result goes back to the browser. Not the query.
// Not the key. Not the other emails on the list.
return res.status(200).json({ message: 'You are on the list!' });
}
return res.status(405).json({ error: 'Method not allowed' });
}
Notice the difference. The browser never sees the Supabase key. It never sees the database query. It never sees the other emails on the waitlist. It sends one email, and gets back one message: "You are on the list." That is the server doing its job — handling the sensitive work behind a locked door.
Real Scenarios Where Vibe Coders Expose Data
These are not hypothetical. These are patterns showing up in real posts on r/vibecoding, r/ChatGPTCoding, and r/webdev every week.
Scenario 1: The Waitlist That Showed Everyone
A builder asks AI: "Build me a waitlist page with Supabase." The AI generates a React component that imports the Supabase client directly and fetches every waitlist entry to show a counter: "247 people have signed up!"
Problem: that Supabase query runs in the browser. The browser has the Supabase anon key. The query fetches all rows. Anyone opening DevTools can see every email on the waitlist in the Network tab. The counter feature exposed the entire database table.
⚠️ The Danger Zone: If your browser's Network tab shows user emails, API keys, database URLs, or full data arrays — your app is leaking data. It does not matter that "it works." It works publicly.
Scenario 2: The Hardcoded API Key
A builder asks AI: "Add Stripe payments to my checkout page." The AI creates a file that includes const stripe = Stripe('sk_live_abc123...') directly in the frontend JavaScript. The checkout works. Payments process. But the secret key — the one that can issue refunds, read customer data, and create charges — is now visible to every visitor.
The publishable key (pk_live_...) is designed to be in the browser. The secret key (sk_live_...) must never leave the server. AI does not always make this distinction on its own.
Scenario 3: The Admin Check That Checks Nothing
A builder asks AI: "Only show the admin panel to admin users." The AI writes a frontend check:
// ❌ DANGEROUS: This "security" check runs in the browser.
// Anyone can open the console and change isAdmin to true.
if (user.role === 'admin') {
showAdminPanel();
}
The problem is that this check runs entirely in the browser. A user can open the console, type user.role = 'admin', and see the admin panel. The real fix is a server-side API that checks the user's role in the database before returning admin data.
Scenario 4: The Price Calculator Anyone Can Edit
A builder asks AI: "Calculate the total with the discount code applied." The AI builds a client-side function that checks discount codes and calculates prices in JavaScript. Someone inspects the code, finds every valid discount code in the source, and also realizes they can modify the price calculation in the console before submitting.
Pricing logic belongs on the server. The browser should send "I want this item with this code" and the server should return the final, validated price.
How to Tell Your AI What Runs Where
The fix is not complicated. You just need to tell your AI where code should live. AI assistants are very good at following explicit instructions about architecture. They are very bad at making security decisions on their own.
Here are prompts that actually work:
Instead of This:
"Build me a waitlist page with Supabase"
Say This:
"Build me a waitlist page. The frontend should only have a
form that POSTs an email to /api/waitlist. The server-side
API endpoint should handle the Supabase insert using the
service key from environment variables. Never import
Supabase in the frontend code. Never expose the waitlist
emails to the browser."
Here are more examples of security-aware prompts:
- For API keys: "Store all API keys in server-side environment variables. The frontend must never contain any key that starts with
sk_or any service role key." - For authentication: "All auth checks must happen server-side. The frontend can check local state for UI display, but the server must validate the token before returning any protected data."
- For pricing: "Calculate all prices and discounts server-side. The frontend sends the cart and discount code. The server returns the validated total. Never trust client-side price calculations."
- For admin features: "The admin panel route can exist in the frontend for navigation, but every piece of admin data must come from a server-side endpoint that verifies admin role before responding."
The pattern is always the same: the browser asks, the server decides.
Common Mistakes AI Makes (and How to Catch Them)
Here is a checklist of things to look for in AI-generated code. If you spot any of these, something probably needs to move server-side.
🔴 Red Flags in Frontend Code
- API keys in JavaScript files. Search your frontend code for strings like
sk_,secret,service_role,private_key. None of those should ever appear in code the browser loads. - Direct database client imports. If you see
import { createClient }from Supabase, Firebase Admin, Prisma, or any database driver in a file that ships to the browser — that is a problem. - Fetching more data than the UI needs. If the browser requests all users but only displays a count, the full list is still visible in the Network tab.
- Client-side role checks as security.
if (user.role === 'admin')in the browser is a UI hint, not security. The server must enforce the same check. - Pricing or discount logic in JavaScript. Any financial calculation in the browser can be modified by the visitor.
- Environment variables prefixed with
NEXT_PUBLIC_orVITE_containing secrets. Those prefixes mean "include in the browser bundle." AI loves using them because they make things work immediately.
How to Check Your Own App
- Open your app in Chrome or Firefox.
- Right-click anywhere → Inspect (or press F12).
- Click the Sources tab. Browse through your JavaScript files. Search for keywords like "key," "secret," "password," "supabase," "stripe."
- Click the Network tab. Reload the page. Click on each request and look at the Response. If you see data that should be private — user emails, full database records, admin details — your app is leaking.
- Check the Console tab. Sometimes AI logs sensitive data during development and forgets to remove it.
If you can see it in DevTools, so can every visitor. That is not a security vulnerability you need to hack. It is how browsers work.
Quick Reference: What Goes Where
| Belongs in the Browser (Client-Side) | Belongs on the Server (Server-Side) |
|---|---|
| UI interactions (clicks, toggles, animations) | API keys and secrets |
| Form display and basic input validation | Database queries (read and write) |
| Page navigation and routing | Authentication and authorization checks |
| Displaying data the server sends | Pricing and payment calculations |
| CSS, HTML structure, visual layout | Email sending, notifications |
Publishable API keys (like Stripe pk_) |
Secret API keys (like Stripe sk_) |
| Analytics tracking (pageviews, clicks) | User data processing and storage |
| Loading spinners and error message display | File processing and uploads to storage |
When in doubt, ask yourself: "Would I want a stranger to see this?" If the answer is no, it goes on the server.
What to Learn Next
Now that you understand the client-server split, here is where to go next to lock down your knowledge:
- What Is an API? — The bridge between your browser code and your server code. This is how the frontend asks the server for things safely.
- What Is an Environment Variable? — Where your API keys actually live on the server. Not in code. Not in config files that get shipped to browsers. In environment variables.
- What Is Authentication? — How you verify who someone is on the server side, not just in the browser.
- Security Basics for AI Coders — The broader picture of keeping your AI-built apps safe, including input validation, CORS, HTTPS, and more.
- What Is JavaScript? — Understand the language that runs on both the client and the server, and what to look for when AI generates it.
- What Is HTML? — The structural layer that lives in the browser. Understanding HTML helps you spot when AI puts data somewhere it should not be.
- What Is React? — Most vibe-coded apps use React. Understanding where React components render (client vs server) is critical for security.
Frequently Asked Questions
Client-side code runs in the user's browser. Anyone visiting your site can see it by opening DevTools. Server-side code runs on a machine you control, and visitors only see the results you send back. Think of it like the front of a house (public) versus the mechanical room (private and locked).
AI optimizes for making things work, not for making them secure. Putting everything in one file that runs in the browser is the fastest path to a functioning feature. The AI is not being malicious — it is being lazy in the same way a new contractor might wire everything to one breaker because it works, even though it is not up to code.
Open your app in a browser, press F12 to open DevTools, and check three places: the Sources tab (search for "key," "secret," or "password"), the Network tab (look at API responses for data that should be private), and the Console tab (check for logged sensitive data). If you can see it, every visitor can too.
Be explicit. Instead of "build me a waitlist page," say "build a waitlist page where the frontend only has a form that POSTs to a server-side API endpoint. The Supabase key must only exist in server-side environment variables. Never import the database client in frontend code." The more specific you are about what stays hidden, the better.
Yes. One hundred percent. Every line of HTML, CSS, and JavaScript that runs in the browser is downloaded to the visitor's computer. They can read it, copy it, and modify it. This is not a security flaw — it is a fundamental part of how the web works. That is exactly why secrets and sensitive logic must live on the server.