The short version: layer cheap checks before expensive ones, and block only what you can prove is bad. Reject invalid syntax and known-disposable domains at the door (both are free and instant), verify the survivors against the live mailbox, and treat role addresses and catch-all domains as flags rather than rejections. Bots are a separate problem from bad addresses — solve them with behavioural signals, not by blacklisting inboxes. Below is each layer, why it exists, and the legitimate edge cases that a naive filter gets wrong.
The three things you're actually blocking
"Fake signup" bundles three distinct problems, and conflating them is why so many filters are either too leaky or too aggressive:
- Invalid addresses — typos and malformed strings (
jane@gmial,test@test) that can never receive mail. Pure waste; remove them. - Disposable / throwaway inboxes — technically valid, deliverable today, and designed to evaporate in an hour. Someone grabbing your lead magnet without giving a real address.
- Bots — automated form submissions that may use perfectly real-looking addresses (or hijacked ones). The address check won't catch these; behaviour does.
Each layer below targets one of these. Run them cheapest-first so you spend a verification probe only on input that survived the free checks.
Layer 1 — kill invalid syntax for free
Before anything hits an API, validate the format. A missing @, a space in the local part, a domain with no dot — these hard-bounce every time and cost nothing to reject. Use the RFC-based rules behind the Email Syntax Validator as your first gate. But keep it honest: syntax validation proves an address could exist, not that it does. Don't over-tune the regex — see the edge cases below, where an aggressive pattern rejects valid customers.
Layer 2 — detect disposable domains
Disposable email services (10-minute-mail-style domains) exist so people can take your free thing without handing over a real inbox. The address works just long enough to click a confirmation link, then it's gone — which means the "user" is unreachable forever and pollutes every metric you have. Detection is a lookup against a maintained list of known throwaway domains, which is what the Disposable Email Checker does. Because it's a free, instant domain lookup, run it before you spend a verification credit. For most signup flows, blocking confirmed disposable domains is safe and correct — a real customer very rarely needs one, and the ones who do are usually testing you, not buying from you.
Layer 3 — verify the mailbox is real
Syntax and disposable checks pass plenty of addresses that still don't exist — a valid-looking address on a real domain whose mailbox was never created, or was deleted. A live verification opens an SMTP conversation with the receiving server and asks whether that specific mailbox would accept mail, without sending anything. Use the Email Verifier for spot checks, or the API at signup. The critical policy, though, is the same one that keeps verification from hurting conversion: block only on a confirmed undeliverable. A risky or unknown result is not proof of a fake — fail open on those and flag them, or you'll reject real people over a catch-all domain or a slow server. The signup-conversion guide covers that UX in depth.
Layer 4 — flag role addresses, don't block them
Role-based addresses (info@, support@, sales@, admin@) route to a shared inbox rather than a person. They carry a higher complaint and unsubscribe risk because whoever reads that inbox didn't personally sign up. The instinct is to block them — but for B2B products, support@ or accounts@ is often exactly the address a real business wants to use. So the right move is to detect and flag, then decide by context. Catch them with the Role-Based Email Checker, tag the record, and let your downstream logic route consumer signups away from role addresses while allowing them for business accounts. The nuance on block-vs-flag-vs-allow lives in the disposable & role-based guide.
Layer 5 — score bots separately
None of the above catches a bot using a real, deliverable address. Bots are a behavioural problem, so use behavioural signals: a hidden honeypot field a human never fills, time-to-submit that's implausibly fast, submission rate from one IP, and IP reputation. Keep this scoring orthogonal to the address checks — a real address with bot-like behaviour should be challenged (a soft step-up, not a hard block), and a suspicious address from an obvious human should still be verified normally. Combining the two signals into one number is how filters end up blocking real users and passing sophisticated bots.
Putting the layers together
Here is the whole gate as one function — free checks first, one probe, then flags and a bot score that never override a legitimate address:
// A layered signup gate: cheapest checks first, then a live probe.
// The ORDER matters — reject obvious junk for free before spending a credit.
async function screenSignup(email, req) {
// 1. Syntax — free, instant. Kills malformed input.
if (!isValidSyntax(email)) return { action: 'reject', reason: 'invalid' };
const domain = email.split('@')[1].toLowerCase();
// 2. Disposable domain — free lookup against a known-throwaway list.
if (isDisposableDomain(domain)) return { action: 'reject', reason: 'disposable' };
// 3. Live verification — one probe. Only BLOCK on a confirmed dead mailbox.
const result = await verifyEmail(email, { timeoutMs: 4000 });
if (result.status === 'undeliverable') return { action: 'reject', reason: 'undeliverable' };
// 4. Role address (support@, info@) — accept, but flag for review.
const isRole = result.subStatus === 'role' || isRoleLocalPart(email);
// 5. Bot signals are orthogonal to the address — combine, don't conflate.
const botScore = scoreBotSignals(req); // honeypot, timing, rate, reputation
return {
action: botScore > THRESHOLD ? 'challenge' : 'allow',
flags: { role: isRole, risk: result.status === 'risky' ? result.subStatus : null },
};
}Legitimate edge cases a naive filter breaks
- Plus-addressing.
jane+signup@gmail.comis a real, deliverable address that Gmail and others route straight tojane@. Users use it to tag where mail came from — never reject it, and never strip the tag as if it were an error. - Legitimate role addresses. A small business genuinely operating from
hello@theircompany.comis a real customer. Flag, route, and decide by product — don't auto-block. - Catch-all business domains. Many Google Workspace and Microsoft 365 domains accept every address, so verification returns
risky/unknown even for a real mailbox. Blanket-blocking catch-all throws away real B2B signups. - Long or unusual-but-valid local parts. Dots, dashes, and long names are all valid. Let the verifier judge deliverability instead of an over-strict regex.
- New or niche TLDs. A domain on a newer TLD isn't automatically fake. Check the actual mailbox, not a hardcoded list of "real" TLDs.
A sensible default policy
For most consumer signups: reject invalid syntax and confirmed disposable domains, reject confirmed undeliverable mailboxes, flag role and catch-all addresses for review, challenge (don't hard-block) high bot scores, and allow everything else. For B2B, loosen the role-address rule to allow-and-flag. Whatever you choose, test each branch deterministically against @test.verifyany.email addresses (undeliverable@, risky@, and so on) so you know exactly what your gate does before it faces real traffic. Then keep the data honest over time with the list-cleaning workflow — even a good gate lets some rot through, and hygiene is what removes it.