How to stop fake signups
To stop fake signups, check every new signup in layers: catch simple bots on the form itself, check the email address, phone number and IP address in the background, combine the results into one risk score, and only then decide whether to let the signup in, give it less, or block it. No single check catches everything, but together they stop most fake accounts without bothering real users.
This guide walks through each layer, with code you can copy, and shows how to react without locking out real customers.
What fake signups look like
- Bots that fill in your signup form automatically, often hundreds of times in a few minutes.
- Throwaway inboxes from disposable email services, used to get your free trial or credits and then disappear.
- Placeholder and typo addresses like
test@test.comoranna@gmial.com, which never receive your onboarding emails. - One person, many accounts, using address tricks such as
anna+2@gmail.comto get the free part again.
They cost real money when every new account comes with free credits or compute. The fake signup cost calculator helps you put a number on it.
Layer 1: stop simple bots on the form
Cheap checks on the form itself stop a large share of scripted signups before any lookup is needed.
A hidden honeypot field
Add a field that people cannot see but simple bots fill in. If it has a value, drop the signup quietly.
<input type="text" name="website" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px" aria-hidden="true">
// on the server
if (req.body.website) return res.status(200).end(); // bot: no account, no error
A minimum time to fill in the form
People need a few seconds to type an email and a password; scripts often need a few milliseconds. Store when the form was shown and reject signups that arrive faster than about two seconds.
Rate limits per IP address
Limit how many signups one IP address can create within a few minutes. Keep the window short, because offices and mobile networks put many real people behind one address.
A CAPTCHA, if you need it
A CAPTCHA or an invisible challenge stops more scripts, but adds friction and does not stop people signing up by hand. Many teams show it only after a rate limit is hit.
Layer 2: check the email address
The email address tells you more than any other field. Check, in this order:
- Syntax: is it a valid address at all?
- Mail server: does the domain have MX records, so it can receive mail?
- Disposable domain: is it a known throwaway inbox service? The guide on blocking disposable emails covers this step in detail.
- Typos and placeholders:
gmial.com,test@,noreply@. Ask the user to correct a typo instead of blocking. - Normalization: remove dots and +tags for Gmail before you check for duplicates.
import { resolveMx } from "node:dns/promises";
async function canReceiveMail(email) {
const domain = email.split("@")[1]?.toLowerCase();
if (!domain) return false;
try {
return (await resolveMx(domain)).length > 0;
} catch {
return false; // no mail server: nobody will read your welcome email
}
}
Then send a confirmation email and unlock free credits only after it is confirmed. This alone makes most throwaway and typo signups worthless.
Layer 3: check the phone number, if you ask for one
A phone number is harder to fake than an email address, but not impossible. Check that it is valid for its country, look at the line type (virtual and VoIP numbers are cheap to get in bulk), and check whether the number was already used by another account. Our free phone number checker shows what this looks like for a single number.
Layer 4: check the IP address
Real people usually sign up from a home, office or mobile connection. Signups from the Tor network or from datacenter and cloud servers deserve a closer look, because that is where scripts run. A VPN is a weaker signal: many honest people use one. The free IP checker shows these signals for any address.
Layer 5: combine the signals and decide
Each signal alone is weak; a VPN or a new mail domain proves nothing. What works is adding them up and acting on the total:
- Low risk: let the signup in as usual.
- Medium risk: let it in, but with a smaller trial, fewer free credits or credits only after email confirmation, and look at it later.
- High risk: block, with a neutral message that does not say which rule was hit.
To spot one person behind many accounts, compare each signup with your recent ones as well. The guide on detecting multiple accounts explains the signals, and stopping free trial abuse shows how to make each extra account worth less.
Common mistakes
- Blocking on one weak signal. A VPN, a shared IP or a forwarding service is not proof of fraud.
- Only checking after the damage. Deleting fake accounts a week later does not bring back the free credits they used.
- Making every real user pay for it. A mandatory card or a CAPTCHA on every visit keeps fakes out, but also many real people.
- Letting your signup depend on the checks. If a lookup is slow or down, let the signup in and check it later, rather than losing a real user.
Doing it with one API call
SignupVet runs layers 2 to 5 in one request: it checks the email, phone number and IP address, compares the signup with your recent ones, and returns a risk score, a recommendation (allow, review or block) and the reasons behind it, such as disposable_domain, mx_records_missing, ip_datacenter_asn, tor_exit_node or signup_velocity_ip.
const res = await fetch("https://www.signupvet.com/score", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.SIGNUPVET_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email, phone, ip: req.ip }),
signal: AbortSignal.timeout(2000),
}).catch(() => null); // fail open: never lose a real signup to a timeout
const { recommendation, reason_codes } = res?.ok ? await res.json() : { recommendation: "allow", reason_codes: [] };
What it does not do: it does not add a CAPTCHA or honeypot to your form (layer 1 stays in your own code), and it has no device fingerprinting. Ready-made setups for Express, FastAPI, Next.js, Supabase, Clerk, Firebase and Better Auth are in the integration guides, and the documentation lists every reason code.
Frequently asked questions
What is a fake signup?
A fake signup is an account that was not created by a real person who wants to use your product: a bot filling in your form, someone using a throwaway inbox to get free credits again, a test address like test@test.com, or one person creating many accounts.
Is a CAPTCHA enough to stop fake signups?
No. A CAPTCHA stops simple scripts, but not a person who signs up by hand with a new throwaway address each time, and paid solving services get around it too. Use it as one layer next to checks on the email, phone number and IP address.
Should I block fake signups or just delete them later?
Check at signup and clean up later. Checking at signup stops free credits and trials from being used before you notice. Clear cases can be blocked right away; borderline ones are better let in with a smaller trial and reviewed later.
Will checking signups annoy real users?
Not if the checks run in the background and only clear cases are blocked. Real users never see an email, phone or IP check. Only friction you add for everyone, such as a CAPTCHA on every form or a mandatory card, is felt by real users.
Check your next signup in one call
SignupVet checks the email, phone number and IP address of every new signup and tells you whether to allow, review or block it. It is free for 250 signups a month, no card needed.