How to Block Disposable Emails on Your Signup Form, Tested
To block disposable emails at signup, reject the domain if it is on a maintained disposable list, then look up its MX records and reject it if the mail is handled by a server that only serves throwaway inboxes. In our test on 1 October 2026, that MX check caught 420 of 445 newly listed disposable domains that a stale list missed.
Most guides stop at "use a blocklist". The interesting questions are how quickly a list goes stale, what catches the gap, and how to avoid blocking real people while you do it. We measured all three.
Why a domain list on its own is not enough
A disposable domain list falls behind the day you copy it. Throwaway services register new domains constantly, and each one works until somebody adds it to a list.
We compared SimpleVerifier's own list of 8,740 disposable domains with the current version of the open-source disposable-email-domains list, fetched on 1 October 2026. They overlap almost entirely: 8,739 of our 8,740 domains appear in it. But the open-source list now contains 457 domains that ours does not. Any form using the older snapshot would have let all 457 through.
The list also rots in the other direction. We resolved MX records for all 8,740 domains:
| Result | Domains | Share |
|---|---|---|
| Has MX records | 5,789 | 66.2% |
| Null MX (accepts no mail, RFC 7505) | 134 | 1.5% |
| Domain does not exist (NXDOMAIN) | 1,869 | 21.4% |
| Exists, no MX record | 838 | 9.6% |
| DNS server failure or timeout | 110 | 1.3% |
More than a fifth of the domains no longer exist. Blocking them costs nothing, since nobody can receive mail there. But it shows the churn: services drop domains and register new ones, and a list is always describing the past.
What an MX check adds
Disposable services run hundreds of domains on a handful of mail servers. Check where a new domain's mail goes, and you can recognise the service even when you have never seen the domain.
Across the 5,789 listed domains with working MX records there were 1,246 distinct primary mail servers, and they are heavily concentrated:
| Top N primary MX hosts | Domains served | Share of listed domains with MX |
|---|---|---|
| 10 | 2,719 | 47.0% |
| 25 | 3,636 | 62.8% |
| 50 | 4,153 | 71.7% |
| 100 | 4,503 | 77.8% |
The single busiest host, email.chatgpt.org.uk, is the primary MX for 470 listed domains. generator.email, tinyhost.shop and emailfake.com each serve hundreds more.
Then we tested whether that knowledge transfers to domains a list has not caught yet. We resolved MX for the 457 domains that were in the open-source list but missing from ours. 445 had MX records. Of those, 420 used at least one mail server that already served a domain on our older list, excluding general-purpose providers (see the next section). The other 25 used either mail servers we had never seen or only shared providers.
In other words, if the form had checked MX hosts as well as domain names, the stale list would have caught 420 of the 445 new domains with working mail (94%) anyway. The new domains were mostly fresh names on the same old servers.
Method, so you can repeat it: queries were made with dnspython 2.8.0 against 1.1.1.1 and 8.8.8.8 with a 6-second timeout, on 1 October 2026. Each domain's MX hosts were recorded; a domain counted as a match if any of its MX hosts appeared among the MX hosts of our 8,740 listed domains, after excluding the shared providers listed below.
The false-positive trap: shared mail infrastructure
Never block on an MX host that also serves ordinary businesses. That one rule separates a useful MX check from one that rejects your best customers.
567 of the 5,789 listed domains with MX (9.8%) route mail through general-purpose infrastructure:
| Provider (MX hostname contains) | Listed disposable domains using it |
|---|---|
cloudflare.net (Cloudflare Email Routing) |
290 |
google.com (Google Workspace / Gmail) |
88 |
above.com (domain parking) |
57 |
mailgun.org |
50 |
registrar-servers (Namecheap forwarding) |
32 |
outlook.com (Microsoft 365) |
17 |
zoho |
13 |
secureserver (GoDaddy) |
10 |
improvmx |
7 |
forwardemail |
3 |
If you added aspmx.l.google.com to a disposable MX set because 51 throwaway domains use it, you would block every company on Google Workspace. The same goes for Cloudflare's routing servers. Those 567 domains can only be caught by name, which is why the list stays as the first layer.
The other false-positive sources worth designing around:
- Privacy relays are not disposable. Apple's Hide My Email, Firefox Relay and DuckDuckGo addresses forward to a real, long-lived inbox. None of
privaterelay.appleid.com,mozmail.comorduck.comis on the open-source list or ours. Treat them as real users with a privacy preference. - Dead domains get re-registered. 1,869 listed domains currently do not exist. If one is bought later by a legitimate business, a list that never removes entries will block its staff. The open-source maintainers say as much: they "cannot guarantee all of these can still be considered disposable", only that "they were disposable at one point in time".
- Missing MX is not the same as no mail. Under RFC 5321, a domain with no MX record falls back to its A record. Rejecting every domain without MX blocks a small number of real setups. Reject on NXDOMAIN and on null MX, which are unambiguous.
- Subdomains of a listed domain. Some services hand out
anything.service.tld. Match the domain and each parent domain against the list, not only the exact string.
A working implementation in Node.js
This is the order we recommend: list lookup (no network), then one MX query with a short timeout, then a decision. It fails open on DNS errors so an outage at your resolver never stops real signups.
import { promises as dns } from "node:dns";
import { readFileSync } from "node:fs";
const BLOCKLIST = new Set(
readFileSync("disposable_domains.txt", "utf8").split("\n").map((d) => d.trim().toLowerCase()).filter(Boolean)
);
// MX hosts that only serve throwaway inboxes. Never put Google, Microsoft,
// Cloudflare or Mailgun here: they host millions of real domains too.
const DISPOSABLE_MX = new Set([
"email.chatgpt.org.uk", "generator.email", "emailfake.com",
"tinyhost.shop", "mail.wabblywabble.com", "mail.mailinator.com",
]);
function onBlocklist(domain) {
const labels = domain.split(".");
for (let i = 0; i < labels.length - 1; i++) {
if (BLOCKLIST.has(labels.slice(i).join("."))) return true;
}
return false;
}
async function withTimeout(promise, ms) {
let timer;
const timeout = new Promise((_, reject) => { timer = setTimeout(() => reject(new Error("ETIMEOUT")), ms); });
try { return await Promise.race([promise, timeout]); } finally { clearTimeout(timer); }
}
export async function checkSignupDomain(email) {
const domain = email.split("@").pop().trim().toLowerCase();
if (onBlocklist(domain)) return { verdict: "block", reason: "domain on disposable list" };
let records;
try {
records = await withTimeout(dns.resolveMx(domain), 3000);
} catch (err) {
if (err.code === "ENOTFOUND") return { verdict: "block", reason: "domain does not exist" };
if (err.code === "ENODATA") {
// RFC 5321 section 5.1: no MX means mail falls back to the A record.
const hasA = await withTimeout(dns.resolve4(domain), 3000).then(() => true, () => false);
return hasA
? { verdict: "allow", reason: "no MX, but A record (implicit MX)" }
: { verdict: "block", reason: "domain cannot receive mail" };
}
return { verdict: "allow", reason: "DNS unavailable, failing open" };
}
const hosts = records.map((r) => r.exchange.toLowerCase().replace(/\.$/, ""));
if (hosts.length === 0 || hosts.every((h) => h === "")) {
return { verdict: "block", reason: "null MX: domain accepts no mail" };
}
if (hosts.some((h) => DISPOSABLE_MX.has(h))) {
return { verdict: "block", reason: `MX ${hosts[0]} serves disposable inboxes` };
}
return { verdict: "allow", reason: "no disposable signal" };
}
for (const email of process.argv.slice(2)) {
console.log(email, await checkSignupDomain(email));
}
Save it as check.mjs beside a disposable_domains.txt file (one domain per line) and run it on Node 18 or later. Our output on 1 October 2026:
$ node check.mjs a@mailinator.com x@foo.mailinator.com a@247chats.com b@gmail.com d@example.com f@github.io
a@mailinator.com { verdict: 'block', reason: 'domain on disposable list' }
x@foo.mailinator.com { verdict: 'block', reason: 'domain on disposable list' }
a@247chats.com { verdict: 'block', reason: 'MX generator.email serves disposable inboxes' }
b@gmail.com { verdict: 'allow', reason: 'no disposable signal' }
d@example.com { verdict: 'block', reason: 'null MX: domain accepts no mail' }
f@github.io { verdict: 'allow', reason: 'no MX, but A record (implicit MX)' }
247chats.com is one of the 457 domains missing from our older list. The name check let it through; the MX check did not.
Two notes on running this in production. Refresh the domain list on a schedule rather than committing it once, and keep DISPOSABLE_MX short and hand-curated: add a host only when every domain you have seen on it is disposable.
What to show the user
Tell people plainly that you need an address they will keep, and let them try again. A blocked signup with a vague error looks like a broken form.
Something like: "We can't accept temporary email addresses. Please use an address you'll still have next month." Do not echo back which list or check matched; it only helps someone rotate to the next domain.
Run the check on the server. A client-side copy is fine as a hint while typing, but anyone can bypass it, and shipping an 8,000-line list to every browser costs page weight for no security.
Where this fits with everything else
Disposable blocking is one layer, not a signup defence on its own:
- Typos such as
gmial.comare a different problem with a different fix (suggest, do not block). See catching email typos at signup. - Plus addressing and Gmail dots let one real inbox create many accounts. That is about normalisation, not disposability: plus addressing and free-trial abuse.
- Bots filling the form at scale need rate limits and a challenge: stopping fake signups without hurting real users.
- Whether the mailbox exists needs an SMTP check, which DNS cannot answer. See checking an address without sending anything.
You can check a single domain by hand with our disposable email checker or see its mail servers with the MX lookup tool.
The short version
- Match the domain, and each parent domain, against a disposable list you refresh automatically.
- Look up MX. Block NXDOMAIN and null MX. Do not block "no MX" without checking the A record.
- Block MX hosts that serve only throwaway inboxes. Never block shared providers like Google, Microsoft or Cloudflare.
- Fail open on DNS errors, and give the user a clear message and a second chance.
The list catches the services everyone knows. The MX check catches the domains they registered last week.
Common questions
What is the best way to block disposable email addresses?
Check the domain against a maintained blocklist first, then look up its MX records and compare the mail servers against hosts known to serve only throwaway inboxes. The list handles the well-known services instantly, and the MX check catches new domains that point at the same infrastructure before any list has added them.
Can I block disposable emails by checking MX records alone?
Not safely. Many disposable domains route mail through Cloudflare, Google or Mailgun, which also host millions of legitimate domains. MX matching only works against a curated set of hosts that serve nothing but throwaway inboxes, and it works best alongside a domain list.
Should I block Apple Hide My Email, Firefox Relay or DuckDuckGo addresses?
Generally no. Those are forwarding services: mail reaches the person's real inbox and they can reply. They are a privacy choice rather than a throwaway one, and the main open-source disposable list does not include them.
How often should a disposable domain list be updated?
As often as you can automate it. In our comparison, the open-source list we checked contained 457 domains missing from our own 8,740-domain list, which otherwise matched it almost exactly, so a list copied into your code once and never refreshed falls behind quickly.
Verify unlimited addresses for $29.99/month
Real SMTP mailbox checks. No credits, no per-email fees.
Get Started