Deliverability

How websites detect temporary email, and how to build that check

Three cheap signals do almost all the work: a public domain blocklist, what DNS says the domain can receive, and how the signup behaves. Here is how to implement each one and test it.

On this page
  1. The three signals that do the work
  2. Step 1: normalize before you look anything up
  3. Step 2: match the domain against a public blocklist
  4. Step 3: ask DNS what the domain can receive
  5. Step 4: score the signup, not just the domain
  6. Step 5: choose block, friction or verify
  7. Step 6: test the gate, and test what it does to you

Most sites run three cheap checks, in this order: is the domain on a public disposable blocklist, does DNS say the domain can receive mail at all, and does the signup look like one human. If you are asking how do websites detect temporary email at any real scale, that is the whole mechanism. Almost nobody probes a mail server, and the ones who try mostly get rate limited for it.

This is a build guide. Normalize, blocklist, DNS, behaviour, then a decision, then tests.

The three signals that do the work

Domain reputation answers “is this a known throwaway”. DNS answers “can this domain receive anything”. Behaviour answers “is this one person or a script”. The first is a set lookup, the second is a single query, the third is data your rate limiter already has. Paid validation APIs and SMTP probing are refinements on top of these three, and they are where most of the cost and most of the false positives live. Every longer answer to how do websites detect temporary email reduces to those three.

Step 1: normalize before you look anything up

Take everything after the last @, lowercase it, and strip a trailing dot. Then reduce to the registrable domain, the name someone actually bought, because blocklists store second-level domains and abuse rides on subdomains.

function registrableDomain(email: string): string {
  const host = email.slice(email.lastIndexOf('@') + 1).trim().toLowerCase();
  return host.replace(/\.$/, '');
}

For mail.example.co.uk you need public-suffix-aware matching, not a naive “last two labels” split. Use a public suffix list library. Getting this wrong is the most common reason a gate silently passes everything.

Step 2: match the domain against a public blocklist

The reference list is the community disposable-email-domains repository, whose disposable_email_blocklist.conf is what PyPI uses to keep throwaway domains out of account registration. Contributors have to show a screenshot of a page generating an address on that domain, and the maintainers still say plainly that they cannot guarantee every entry is disposable today.

Vendor the file into your repo and refresh it on a schedule. Do not fetch it over the network on the signup path.

const blocked = new Set(
  fs
    .readFileSync('disposable_email_blocklist.conf', 'utf8')
    .split('\n')
    .map((l) => l.trim())
    .filter(Boolean),
);

const isListed = blocked.has(registrableDomain(email));

Know what this misses. New domains appear before any list carries them, a custom domain never appears at all, and forwarding or alias services get swept in beside genuine disposables. A blocklist hit is evidence, not proof.

Step 3: ask DNS what the domain can receive

dig +short MX example.com

Three outcomes matter. No MX and no A or AAAA record means nothing can accept mail there, which in practice is a typo. A single MX with preference 0 and a target of . is a null MX under RFC 7505: the operator has declared that the domain accepts no mail, so reject it straight away instead of queueing. And an MX host shared across hundreds of unrelated domains is the strongest live signal you get, because disposable providers rotate domain names far faster than they rebuild the server behind them.

Stop there. RFC 5321 lets a server disable VRFY, and nearly all of them do. Catch-all domains answer 250 to every RCPT TO, so a probe tells you nothing, and probing at volume is a fast route onto a blocklist yourself.

Step 4: score the signup, not just the domain

The last signal is the request itself: the registration age of the domain via RDAP, local parts with high entropy and no dictionary words, a burst of signups from a single ASN, and whether the provider publishes its own domain list. InboxDome publishes its pool through list_domains, one of the seven tools on its MCP server, so a site that does not want test traffic can filter it deliberately.

Step 5: choose block, friction or verify

The usual mistake is treating this as a boolean. Verify-then-grant beats reject-at-the-form. Temporary inboxes expire on their own: an InboxDome consumer inbox lasts 24 hours by default, extendable to at most 7 days, with a cleanup job running every 15 minutes. So anything you need to deliver later fails without your help. Gate the expensive actions, free credit, refunds, posting a review, behind a confirmed address instead.

If you do reject, say why. Returning “invalid email address” for what is really a policy decision sends someone into a loop of retyping an address that works perfectly well.

Step 6: test the gate, and test what it does to you

Build a fixture table first: a listed domain, a null MX domain, a domain with no records, an uppercase address, a plus-addressed one, and a real address that must pass. Those are unit tests and they run in milliseconds.

Then test the flow the gate protects. Create a real inbox, submit the form, wait for the verification mail, assert on it:

curl -X POST https://inboxdome.com/v1/inboxes \
  -H "Authorization: Bearer $INBOXDOME_KEY"

curl "https://inboxdome.com/v1/inboxes/$INBOX_ID/wait?for=otp&timeoutSeconds=30" \
  -H "Authorization: Bearer $INBOXDOME_KEY"

Assert on what SPF, DKIM and DMARC each actually prove while you are in there, because a signup gate is worthless if the confirmation mail it depends on lands in spam. The Email Testing API for signup and verification flows covers both in one call, and the Playwright email testing guide has the browser half.

Then the catch that bites everyone who ships a gate: your own tests will trip it, since a shared temporary-mail pool is exactly what the blocklist contains. Allowlist your testing domain outside production, or take the cleaner route and run email tests on your own domain, where reputation and detection are both yours. The retention figures behind the expiry argument above are on the temporary inbox lifetime page.

One boundary is worth stating out loud. InboxDome is receive-only, and it is for testing flows you own or are authorized to test. Building the detector and testing your own signup are two halves of the same job, not opposite sides of it.

Disclosure: InboxDome publishes this blog and makes the products it mentions. We describe what they do rather than promise results.

Key takeaways

  • Three signals do the work: a public disposable-domain blocklist, an MX lookup, and signup behaviour. Everything else is refinement.
  • Normalize to the registrable domain before any lookup, because blocklists store second-level domains.
  • A null MX record (RFC 7505) is a definitive "this domain accepts no mail". A missing MX is usually a typo.
  • Do not probe over SMTP. VRFY is disabled almost everywhere and catch-all domains accept every recipient.
  • Prefer verify-then-grant over a hard block: temporary inboxes expire on their own, and hard blocks hit real alias users.

Sources

  1. disposable-email-domains blocklist (GitHub) — The community list used by PyPI and many libraries. Maintainers state they cannot guarantee every entry is still disposable.
  2. RFC 7505: A Null MX No Service Resource Record — Defines the single MX with preference 0 and a "." target that means the domain accepts no mail.
  3. RFC 5321: Simple Mail Transfer Protocol — MX-based routing, and why servers are allowed to disable VRFY.

Links last checked August 31, 2026.

Frequently asked questions

Can a site tell a temporary address from a permanent one with certainty?

No. Detection reads the domain, not the person. Someone using a custom domain on a mailbox they delete tomorrow is indistinguishable from a lifelong user, so treat the result as a risk score rather than a verdict.

Does blocking disposable domains actually stop fraud?

It raises the cost a little. Determined abusers move to custom domains or alias services that no list carries, while legitimate users who forward mail through an alias get locked out. Verification plus rate limiting does more work than the blocklist.

Should I check MX records on the signup form itself?

Yes. One MX lookup is cheap, catches typos and null MX domains, and can run inline. Cache the answer per domain and never fail a signup because a DNS query timed out.

Will my own email tests get blocked by my disposable-email check?

If they run on a shared temporary-mail pool, yes, because those domains are exactly what the blocklist contains. Allowlist the testing domain in non-production, or run the tests on a domain of your own.

InboxDome TeamEmail infrastructure builders

The InboxDome team runs the temporary-email service and email-testing API this blog belongs to. We write about disposable inboxes, email privacy and testing transactional email, based on operating the infrastructure ourselves.