Building a Disposable Email Blocklist: What 15,000+ Providers Taught Us

hangrydev ·

180,000 Domains and Counting

Last Tuesday we added 47 new disposable email domains to our detection system. By Friday, 12 of them had already rotated out and been replaced by fresh ones. The week before that, 63 new domains. The week before that, 51.

That’s the reality of disposable email detection in 2026. It’s not a list you build once. It’s an arms race you either keep up with or lose.

We’ve tracked over 180,000 disposable email domains across 15,000+ distinct providers. The open-source disposable-email-domains repo on GitHub has around 5,000 entries. That gap tells you everything about why static blocklists fail at scale.

How Disposable Email Services Actually Work

Most developers think of disposable email as “Guerrilla Mail and a couple copycats.” The real ecosystem is far bigger and more varied.

The simplest providers register a batch of domains, point them all at one mail server, and expose a web UI where anyone can read incoming mail for any address. No signup required. Temp-mail.io, Guerrilla Mail, and dozens of clones follow this model. One MX record, hundreds of domains.

Then there are API-generated addresses. Services like Mailinator and its successors offer programmatic access. Bots can generate thousands of unique addresses per minute without touching a browser. These power fake account creation at scale. AtData’s January 2025 research found that 46% of all disposable domains now fall into what they call the “hyper-disposable” category: domains with lifespans under seven days.

The third category is alias-based. Apple’s Hide My Email generates @privaterelay.appleid.com addresses. Firefox Relay uses @mozmail.com. SimpleLogin lets users create unlimited aliases on their own domains. All three forward to a real inbox. These aren’t throwaway accounts. They’re privacy tools used by paying customers. Blocking them is a different decision than blocking tempmail.ninja.

Why Static Blocklists Fail

Here’s a question every developer building email validation asks eventually: can’t I just maintain a list?

You can. For about a week.

The disposable-email-domains GitHub repo is community-maintained and well-vetted. Every domain goes through a PR review. False positives stay low. But at around 5,000 entries, it covers under 3% of the active disposable ecosystem. The rest rotates faster than any volunteer community can track.

Some providers register 50 to 100 domains specifically to evade blocklists. Rotate Mail refills new domains every seven days and cycles through roughly 1,000 at a time. By the time a domain gets reported, reviewed, merged, and deployed, the provider has already moved on.

A static list catches the obvious players. Guerrilla Mail, Mailinator, Yopmail. The domains everyone already knows about. It won’t catch the long tail, and the long tail is where the abuse actually lives.

# Static blocklist approach - catches the obvious, misses the rest
DISPOSABLE_DOMAINS = File.readlines("disposable_domains.txt").map(&:chomp).to_set

def disposable?(email)
  domain = email.split("@").last&.downcase
  DISPOSABLE_DOMAINS.include?(domain)
end

disposable?("[email protected]") # => true
disposable?("[email protected]") # => false (missed)
# Python equivalent - same limitation
disposable_domains = set()
with open("disposable_domains.txt") as f:
    disposable_domains = {line.strip().lower() for line in f}

def is_disposable(email: str) -> bool:
    domain = email.rsplit("@", 1)[-1].lower()
    return domain in disposable_domains

is_disposable("[email protected]")  # True
is_disposable("[email protected]")  # False (missed)
// Node.js - fast Set lookup, same coverage gap
const fs = require("fs");

const disposableDomains = new Set(
  fs.readFileSync("disposable_domains.txt", "utf-8")
    .split("\n")
    .map(d => d.trim().toLowerCase())
    .filter(Boolean)
);

function isDisposable(email) {
  const domain = email.split("@").pop().toLowerCase();
  return disposableDomains.has(domain);
}

isDisposable("[email protected]"); // true
isDisposable("[email protected]"); // false (missed)

These snippets work. They’re fast. They’ll catch the top providers. But they represent a point-in-time snapshot, and point-in-time snapshots go stale the moment you deploy them.

Pattern Detection: Thinking Beyond the Domain Name

The smarter approach doesn’t ask “is this domain on our list?” It asks “does this domain behave like a disposable service?”

MX Record Fingerprinting

Disposable providers are lazy about infrastructure. A provider running 200 domains typically points them all at the same 2 to 3 mail servers. Those MX records are a fingerprint.

When you see mx1.temp-service-backend.com handling mail for throwaway-alpha.com, burner-beta.net, and disposable-gamma.xyz, you don’t need all three on your blocklist. You need the MX pattern. Any new domain pointing at that same server is guilty by association.

# MX-based disposable detection
require "resolv"

KNOWN_DISPOSABLE_MX = Set.new([
  "mx.temp-service-backend.com",
  "mail.throwaway-infra.net",
  # ... hundreds more MX hostnames
])

def disposable_by_mx?(domain)
  mx_records = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX)
  mx_hosts = mx_records.map { |r| r.exchange.to_s.downcase }
  mx_hosts.any? { |host| KNOWN_DISPOSABLE_MX.include?(host) }
rescue Resolv::ResolvError
  false
end

This catches domains that aren’t on any public blocklist yet. The infrastructure gives them away.

Registration Date Analysis

Legitimate businesses don’t register a domain and start receiving email the same day. Disposable services do. A domain registered 48 hours ago with no website, no SPF record, and an MX pointing to known disposable infrastructure? That’s not a real company.

WHOIS data (or RDAP, its replacement) gives you the creation date. Domains under 30 days old that lack SPF, DKIM, and DMARC records carry significantly higher risk. Stack that signal with MX fingerprinting and you’ve got a detection layer that doesn’t depend on any list at all.

Subdomain Patterns

Some providers don’t even register new domains. They use subdomains. random123.tempservice.com, another456.tempservice.com. The parent domain stays the same, but each subdomain acts as its own disposable namespace.

Catching these requires parsing the subdomain structure and checking the parent domain against known providers. A simple suffix check won’t cut it because legitimate companies also use subdomains for email (think mail.department.bigcorp.com).

Open Source vs. API: The Coverage Gap

The open-source approach has clear benefits. It’s free, transparent, and auditable. The disposable-email-domains repo has been battle-tested since 2014 and rarely produces false positives.

But coverage matters. Here’s what the numbers look like:

Approach Domain coverage Update speed False positive rate
disposable-email-domains (GitHub) ~5,000 Community PRs (days to weeks) Very low
Aggregated open-source lists ~110,000 Daily automated pulls Moderate
API-based detection (MailCop) 180,000+ Real-time Low

The aggregated lists get close on raw numbers but sacrifice quality. Automated scrapers pull in domains without verification, and false positives creep in. Block someone’s legitimate custom domain because an automated scraper flagged it? That’s worse than letting a disposable through.

API-based detection combines the list with live analysis. Does the domain resolve? What do the MX records point to? When was it registered? Does it share infrastructure with known disposable services? Those questions get answered in real time, not whenever a maintainer reviews a pull request.

MailCop’s Multi-Layer Approach

We don’t rely on a single signal. Our email validation API runs disposable detection as one layer in a stack.

First pass: domain lookup against our maintained database of 180,000+ known disposable domains. This catches the obvious ones in under 5ms.

Second pass: MX fingerprinting. If the domain isn’t in our database, we check whether its mail servers match known disposable infrastructure. This catches newly registered domains from existing providers before any blocklist picks them up.

Third pass: domain characteristic analysis. Registration date, DNS authentication records, hosting patterns. A domain that’s 3 days old with no SPF record and an MX pointing to a VPS in a datacenter known for hosting disposable services gets flagged, even if we’ve never seen it before.

The result is detection that doesn’t go stale. When a provider registers 50 new domains tomorrow, we don’t need someone to manually add each one. The infrastructure fingerprint catches them automatically.

For developers building signup flows, this means your Shopify checkout validation or SaaS registration form stays protected without you maintaining a single list. The API call handles it.

What Does This Look Like in Practice?

Here’s the API-based approach compared to the static list:

# API-based detection - catches what lists miss
result = MailCop.validate("[email protected]")

if result.disposable?
  # Blocked - even though this domain is 6 hours old
  render json: { error: "Please use a permanent email address" }, status: 422
end
# Python - same API, full coverage
import requests

def check_disposable(email: str) -> bool:
    resp = requests.post(
        "https://api.truemail.io/v1/verify",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={"email": email}
    )
    return resp.json().get("disposable", False)
// Node.js - async check with timeout
async function checkDisposable(email) {
  const resp = await fetch("https://api.truemail.io/v1/verify", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.TRUEMAIL_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email }),
    signal: AbortSignal.timeout(3000),
  });

  const result = await resp.json();
  return result.disposable;
}

The code isn’t more complicated. The coverage is dramatically better. And you’re not shipping a text file that’s already outdated.

The Privacy Question

Not every disposable address is abuse. Apple’s Hide My Email creates unique @privaterelay.appleid.com forwarding addresses for iCloud+ subscribers. Firefox Relay does the same with @mozmail.com. These are privacy-conscious users with real intent to receive your emails.

Blocking them is a product decision, not a technical one. Do you want to prevent all temporary addresses, or just the ones used for throwaway signups? MailCop’s API distinguishes between disposable services and privacy relays, so you can make that call per use case.

A free trial signup? Block disposable, allow privacy relays. An e-commerce checkout? Allow both, because a paying customer using Hide My Email is still a paying customer.

The Arms Race Doesn’t Stop

Disposable email providers will keep registering new domains. Hyper-disposable services will keep cycling through them faster. The 180,000 domains we track today will be 200,000 by year’s end. Probably more.

Static blocklists had their moment. They’re still useful as a first layer, and the open-source community maintaining them does real work. But if you’re relying on a text file to protect your signup flow in 2026, you’re playing catch-up against opponents who automate domain rotation faster than humans can type pull requests.

The state of the art is multi-layer detection: known domains, MX fingerprinting, domain analysis, and live infrastructure checks. All running in real time, all updating continuously. That’s what it takes to stay ahead.

How long until your current blocklist falls behind?