Skip to content

What AI website builders leak, and why it is not their fault

·4 min read

If you built a website with an AI tool in the last year, there is a good chance it published something you did not intend. Not because the tool is bad, and not because you did anything wrong — but because the fastest path from "I want a feature" to "the feature works" runs directly through the browser, and the browser is public.

The shape of the problem

Every one of these tools is optimised for the same thing: getting from a prompt to something that runs. That is genuinely valuable, and it is why they work. But "it runs" and "it is safe to publish" are different tests, and only the first one gets applied while you are building.

The specific mechanism is almost always the same. Your application needs to talk to something — a database, a payment provider, an email service — and talking to it requires a credential. The shortest route is to reference that credential from the code that is already running, which is the code in the browser. It works immediately. Nothing warns you. And now the credential is in a file every visitor downloads.

This is not a bug anybody introduced. It is what happens when the boundary between "runs on my server" and "runs on my visitor's machine" is invisible in the tool you are using.

The five things that turn up most

In roughly the order we see them, and all five are things you can check today.

A secret API key compiled into the JavaScript bundle. Usually a payment provider or a database. The giveaway is a variable name carrying a public prefix — NEXT_PUBLIC_, VITE_, REACT_APP_ — on something that should never have had one.

An environment variable file served as a static asset. Requesting /.env or /.env.production returns a plain-text file with your database password in it. Nothing about the site suggests this; you have to ask for the path.

A hosted database with its access rules never switched on. With Supabase or Firebase, the database is reachable from the browser by design, so the protection is row-level security. A table created during development, when the policy felt like a later problem, is readable by anyone.

No security header at all. Nothing sets them by default and nothing breaks without them, so they are absent on almost every generated site. Their absence is what lets someone frame your real site inside their fake one.

Pages carrying a noindex tag from the staging phase. Correct while you were building, invisible afterwards, and total — a page with it cannot rank for anything.

Why the publishable key is not the dangerous one

This is the distinction that causes the most damage, because the two things look identical.

Most services issue two kinds of credential. One is designed to be visible in the browser — Stripe calls it publishable, Supabase calls it the anon key — and it is safe there, because the service knows it is public and restricts what it can do. The other bypasses those restrictions entirely. Stripe calls it the secret key; Supabase calls it the service role key.

An AI tool asked to make a database call work will reach for whichever key makes the error stop. The service role key always makes the error stop, because it can do everything. That is exactly why it must never leave your server.

If you find a leaked key, do this in this order

The order matters more than the steps. Getting it wrong means doing the work and staying exposed.

  1. Rotate the key first. Go to the provider and issue a new one, revoking the old. Until you do this, the exposed key works — moving it out of your code changes nothing while the old value is still valid. This is the step people skip, and skipping it makes the rest pointless.

  2. Then move it server-side. Remove the public prefix, and move any call that uses the key into a server route or server action. Rebuild and redeploy. Purge your CDN cache too — the old bundle can survive at the edge for a while after deploy.

  3. Then check what was done with it. Look at the provider's logs for the period the key was live. For a payment provider that means payments and refunds; for a database, unusual reads. Most of the time there is nothing, and it is worth the ten minutes to know.

  4. Then look for the others. A deployment that published one key has usually published more than one, because the same shortcut was taken more than once. Search your built output for the string patterns your providers use — sk_live, service_role, and so on.

The uncomfortable part

None of this is visible from the front of your website. The site loads, the pages look right, the checkout works. A leaked key, a downloadable environment file and four noindexed pages all render perfectly.

That is the entire reason this class of problem persists: the feedback loop that would tell you is missing. You would have to think to look, and there is nothing prompting you to think to look.

So the practical advice is simply to look, once, deliberately — whether with our tool, a competitor's, or by hand with the checks above. It takes a few minutes and the answer is usually reassuring. When it is not, it is the kind of thing you want to find before somebody else does.

Check your own site

Everything described here, checked automatically in about a minute. No account, no card, read-only.

Audit my website free