Skip to content

How Do I Stop Bots From Creating Fake Accounts on My App?

6 min read

Bots are creating fake accounts on your app because your signup form is open to the internet with nothing standing between a script and your database. The short answer: add three cheap layers, a CAPTCHA on the signup form, real email verification before an account counts, and a rate limit per IP. Together they stop the vast majority of automated signups without blocking a single real user.

Why this happens

Your AI agent built exactly what you asked for: a form that takes an email and a password and creates an account. That is the whole job, and in the demo it works beautifully. What the agent did not build, because you did not ask, is everything that assumes the person filling in the form is a person.

Bots find open signup endpoints automatically. They scan the web for forms and public APIs, then hammer them with scripted requests. A fake account costs them nothing and can win them a lot: free-tier credits, a foothold to send spam, inflated numbers they can sell, or just the fun of filling your database with junk. If your app calls a paid service on signup, a welcome email, an AI greeting, an SMS code, every fake account also spends your money.

How to check

You do not need tooling to spot this. Look for the signals:

  1. Open your users table. Sort by created date. Bot signups arrive in bursts, dozens in the same minute, often with gibberish emails or a pattern like name+001@, name+002@ on the same domain.
  2. Check whether unverified accounts can do anything. If a brand-new user can log in and use the app before clicking a link in an email, your account count is fiction.
  3. Look at your email or billing dashboard. A spike in sent emails, SMS codes, or API calls with no matching real growth means something is signing up on repeat.
  4. Try it yourself. Open your signup form, submit it, and watch the network tab. If nothing challenges you and no verification step blocks the account, nothing challenges a bot either.

The fix

  1. Add a CAPTCHA to the signup form. Cloudflare Turnstile and hCaptcha are free and mostly invisible to real users. Crucially, verify the token on the server, not just in the browser, a client-only check is trivial to skip.
  2. Require email verification before an account is usable. Most platforms have this built in. On Supabase, enable "Confirm email" in Authentication settings. On Firebase, gate access on emailVerified. An unverified account should be able to do nothing.
  3. Rate-limit the signup endpoint. Cap signups per IP per hour at the edge (Vercel, Cloudflare) or in your API route. A human signs up once; a script tries hundreds.
  4. Add a honeypot field. Put a hidden input real users never see and bots fill in. Reject any submission where it is filled. It costs nothing and catches the lazy bots for free.
  5. Delay the expensive stuff. Do not send the paid email, SMS, or AI call until the email is verified. That way a fake account never costs you money.

The trap to avoid

Do not lean on a single client-side CAPTCHA and call it done. A widget that only runs in the browser is decoration: a bot talks straight to your API and never loads your page. The check that matters is the one on your server, where you confirm the CAPTCHA token, the verified email, and the rate limit before you create the account. One server-side layer beats three client-side ones.

Where this fits

Bot signups are one of the first things a real app meets on the open internet, and they are invisible until the day they arrive in bulk. The free Readiness Report checks your live signup flow for exactly this: whether your form can be scripted, whether unverified accounts count, and whether a single bot can run up your bill. It pairs closely with stopping one user running up your OpenAI bill, since the same open endpoint leaks money as well as fake users. If you would rather have the fixes handed to you in order, that is what the Finishing Pass is for, and it is part of the wider question of whether your app is secure enough to launch.