The Hidden Cost of reject_unverified_sender: Why Sender Callouts Get You Blocklisted
Sender address verification looks like a free anti-spoofing win, but it turns your MX into an outbound probe cannon aimed at servers you don't control — earning backscatter listings, weaponizing you in joe-job attacks, and rejecting legitimate mail. Here's the mechanism and the safe replacement.
EvilMail TeamJuly 25, 202610 min read
Someone on a mailing list — or a Stack Overflow answer from 2011 that still ranks — tells you to drop reject_unverified_sender into your Postfix config to kill spoofed senders. "Only accept mail from senders that actually exist." You test it, a forged MAIL FROM gets bounced, it feels like a free win, and you move on.
Two weeks later your MX is sitting on ips.backscatterer.org, half your inbound from a partner domain is deferring for reasons nobody can explain, and — the part you won't find out about until an abuse desk emails you — an attacker has been quietly using your server to flood a bank's mail infrastructure. That one line did all three. This is what sender address verification actually costs, and what to do instead.
What Postfix is actually doing behind that one line
reject_unverified_sender (also called sender callout, callback, or sender address verification) hands the incoming envelope sender to Postfix's verify(8) service. To decide whether [email protected]
reject_unverified_sender Risks and Safer Ways to Validate Senders | evilmail — EvilMail Blog
is deliverable,
verify(8)
opens a
fresh outbound SMTP session to example.com's MX
and runs a probe transaction — no message body, just an interrogation:
MAIL FROM:<double-bounce@your-mx> # from $address_verify_sender
RCPT TO:<[email protected]> # the claimed sender
# read the 250 or 550...
RSET
QUIT
A 250 on the RCPT means "mailbox exists, sender looks real, accept the message." A 550 means "reject it." The verdict lands in the address verification map (address_verify_map, an lmdb or btree file) and is reused per the refresh timers — address_verify_positive_refresh_time defaults to 7 days, address_verify_negative_refresh_time to 3 hours.
The operational implication is the whole problem. Every time a novel sender hits your server, you generate new outbound SMTP traffic to a machine you have no relationship with, and you do it synchronously in the RCPT stage of the inbound transaction. Your acceptance latency is now hostage to a stranger's MX. If their server is slow, greylisting, or tarpitting probes, your legitimate inbound sender sits there waiting — or gets rejected — because of how *someone else's* server chose to answer *your* uninvited probe.
The three ways this burns you
1. Backscatter listing. A stream of probes aimed at addresses that may not exist is, to a blocklist detector, indistinguishable from a broken server spewing misdirected bounces. UCEPROTECT's Backscatterer BL (ips.backscatterer.org, positive answer 127.0.0.2) explicitly lists servers that emit callout and sender-verification probes, not just classic backscatter. Once you're in that zone, every receiver that checks it — and plenty do, in postscreen or reject_rbl_client — blocks the mail *you* send. You configured an inbound feature and it torched your outbound reputation.
2. Reflection and amplification — the joe-job. This is the non-obvious one, and it's the worst. An attacker sends you a message forged as MAIL FROM:<[email protected]>. Your MX dutifully fires a verification probe at bank.com's MX to check that mailbox. Now scale it: a spam run of thousands of messages, every one forging a different address @bank.com. Your server obediently emits thousands of probe connections at a single victim domain — one in, one out, all originating from your trusted IP. You've become a DDoS reflector, and because the connections carry *your* address, the victim's operators list and blame you while the actual attacker stays invisible.
3. False rejects on legitimate mail. The best-run senders are exactly the ones that break callouts. Greylisters (postgrey, greylistd) return a 4xx to any first-contact connection on principle — and your probe is a first-contact connection. Rate limiters throttle unknown probers. Plenty of servers reject or tempfail the double-bounce sender Postfix probes with, or refuse a null sender outright. Postfix caches whatever verdict it got: a cached 450 or 550 becomes a rejection of the genuine message minutes later. The result is perverse — you bounce mail from the cautious, correctly-configured senders you most want to hear from, while sloppy spammers with wide-open MXes sail through verification.
Why the internet stopped trusting callouts
Sender callout verification has been treated as abuse-adjacent for well over a decade. The objection is structural, not stylistic: it externalizes your spam-filtering cost onto third parties. Every probe you send is CPU, a socket, and a log line consumed on a server run by someone who never agreed to help you filter your mail — and, as shown above, it's trivially turned into a weapon.
Postfix's own ADDRESS_VERIFICATION_README warns, in Wietse Venema's words, that unrestricted sender verification can get your site blacklisted. That's the author of the software telling you not to point this feature at arbitrary remote senders.
One critical distinction before we fix it: sender callout is not the same thing as recipient verification. reject_unverified_recipient probing *your own* delivery transport — checking a mailbox exists on your Dovecot/LMTP backend before you accept mail for it — is entirely legitimate. You own that infrastructure; there are no strangers and no backscatter. The problem is specifically probing servers you don't control.
Do this instead, part 1: validate the domain, not the mailbox
You don't need to know a mailbox exists to throw out most forged garbage. You need to know the sender *domain* is real and resolvable, and that costs one cached DNS lookup — zero outbound SMTP.
Here's a sender restriction block that does the cheap work and nothing reckless:
smtpd_sender_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_non_fqdn_sender
reject_unknown_sender_domain # DNS-only: A/MX must exist, no remote probe
reject_sender_login_mismatch
reject_unknown_sender_domain only asks "does this domain have a valid A or MX record?" — one lookup, cached by your resolver, no packet ever sent to the sender's mailserver. It rejects MAIL FROM:<[email protected]> outright. reject_non_fqdn_sender kills the malformed envelopes spammers love. reject_sender_login_mismatch stops your own authenticated users from spoofing each other. None of these touch a third-party MX.
Do this instead, part 2: authenticate identity with SPF/DKIM/DMARC
The question you actually wanted answered was never "does this mailbox exist?" It was "is this message authorised by the domain it claims to come from?" A callout can't tell you that — a mailbox existing says nothing about who's allowed to send as it. SPF, DKIM and DMARC answer the real question, and they do it with signatures and published policy instead of live interrogation.
The reject decision for identity belongs to DMARC policy, not to your probe logic. When the sender domain publishes p=reject and the message fails alignment, OpenDMARC tells you to drop it — on the domain owner's explicit instruction. That's a completely different thing from you unilaterally deciding a stranger's mailbox looked wrong.
If you're building this fresh in 2026, skip stitching individual milters together and run rspamd as the consolidated scorer. It evaluates SPF, DKIM, DMARC, RBLs, greylisting and content in one weighted verdict, so no single signal — least of all a hard callout — gets to reject mail on its own. That holistic scoring is exactly what a live probe can't do: a probe is a binary yes/no on the wrong attribute.
When a callout is genuinely fine
To be fair to the mechanism: reject_unverified_recipient pointed at your own delivery transport is legitimate and useful. Verifying that [email protected] actually has a mailbox on your Dovecot/LMTP backend before you accept the message spares you from generating a bounce later, and you're probing infrastructure you own — no third parties, no backscatter exposure, no reflection surface. The rule of thumb is one line: probe your own infrastructure, never probe strangers.
The evilmail take
At temp-mail and high-inbound scale this isn't a close call. We ingest a firehose of mail addressed to disposable addresses, which means a huge fraction of senders are novel — so reject_unverified_sender would fire a fresh outbound probe for very nearly every message, turning our MX into a continuous probe cannon. That's latency suicide and a standing invitation to mass blocklisting. We validate sender domains at the DNS layer, lean on DMARC alignment and rspamd's weighted scoring, and never open a socket to a stranger to decide whether to accept a message. That's operational reality, not a stylistic preference.
Checklist
Grep every restriction class in your main.cf and remove any callout now: postconf -n | grep -n unverified_sender.
Add reject_non_fqdn_sender and reject_unknown_sender_domain to smtpd_sender_restrictions.
Wire policyd-spf plus OpenDKIM/OpenDMARC milters; let DMARC p=reject make the identity call. Or run rspamd and let it score.
Keep reject_unverified_recipientonly if it targets your own delivery transport.
Check your sending IP on both zones: dig +short <reversed-ip>.ips.backscatterer.org and dig +short <reversed-ip>.zen.spamhaus.org. A 127.0.0.2 on the backscatterer zone means a callout already burned you — request delisting once the probe is gone.
If some legacy corner *forces* you to keep a callout, harm-reduce it: set address_verify_sender = [email protected] (never <>, which remote servers reject and greylist), and cap the wait with address_verify_poll_count = 3 and unverified_sender_reject_code = 550.