In short: I'm building an email verification service that works via live SMTP probing. One day iCloud started rejecting my server with 550 ... rejected due to listing in Spamhaus — even though my server had never sent a single email. What follows is an investigation with a wrong hypothesis, a humbling config discovery, a bureaucratic ticket rejection, and an unexpectedly happy ending. With lessons that are worth more than the incident itself.
What I'm building
An email verification service: does this mailbox actually exist? Syntax and MX checks are the warm-up; the real question is answered by an SMTP probe: connect to the recipient's mail server, walk the dialogue up to RCPT TO:
Keep that in mind for the rest of the story: not a single message ever left this server.
The day iCloud stopped talking to me
At some point, probes to iCloud started failing with a verbatim:
550 5.7.1 Mail from IP 207.180.207.175 was rejected due to listing in Spamhaus
I checked zen.spamhaus.org — and yes, my IP was listed as CSS (Composite Snowshoe Spam), return code 127.0.0.3. "Snowshoe spam" is when a spammer spreads their sending thinly across many IPs so that no single one stands out. I had just been filed under that category.
Here's the first trap worth knowing if you ever check yourself against blocklists: Spamhaus does not serve queries coming through public resolvers. Ask through 8.8.8.8 or 1.1.1.1 and you get 127.255.255.x — "query refused" — which is trivially easy to misread as "clean". And an empty answer means both "clean" and "the server didn't respond". Both mistakes fail in the falsely reassuring direction, so I wrapped the check in a script that distinguishes all three states instead of a one-liner.
I went through eighteen blocklists: clean everywhere else, including UCEPROTECT levels 2 and 3, which list entire ranges and autonomous systems. So my hosting provider's range wasn't under a blanket ban — the problem was mine, personally.
The machine itself was clean too: the local MTA listens on loopback only, the queue was empty. I wasn't an open relay.
The wrong hypothesis, or how I treated a disease I didn't have
My first model of the cause looked perfectly logical: CSS was issued for a behavioral pattern — my server knocks on dozens of unfamiliar SMTP servers per hour with short connections that drop right after RCPT. From a telemetry point of view, that genuinely resembles reconnaissance before a spam run.
So I built a quota: a hard cap on how many new domains per hour the service may contact at all. Engineering-wise it's the right thing — hammering hundreds of strangers per hour is bad neighborship regardless of anything. But, as you'll see below, that was not why I got listed. A classic lesson about unverified hypotheses: I treated a disease I didn't have, exhaled with satisfaction, and waited for the listing to expire.
It didn't.
The ticket, and the reply that made me blush
Spamhaus delisting is free, so I filed a ticket — if nothing else, to get an answer I couldn't get any other way. Two submission gotchas that may save you an hour: they won't accept requests from freemail addresses (gmail and the like), and the domain of the requester's address must match the domain in your IP's PTR record.
The reply came within half an hour, and the cause was not the one I had assumed:
IP: 207.180.207.175
rDNS: mail.proofmailapi.com
HELO: proofmail.207.180.207.175.sslip.io
There it is. A generic-domain HELO. sslip.io is an "any IP as a domain name" service that staging setups use: handy while you don't have real DNS. My service once lived on such a staging box, and when it moved to the production domain I updated the PTR and SPF — and forgot the HELO in the config. To a receiving server, a host introducing itself via sslip.io is indistinguishable from the thousands of infected machines that do exactly the same.
A remote MTA checks four things about you within the first milliseconds of the dialogue, and all four must agree:
PTR — the reverse record, IP → name.
FCrDNS — that name must resolve back to the same IP.
HELO — the name you introduce yourself with must match the rDNS.
MAIL FROM — the sender's domain must have an MX; some servers go further and do a sender callout — they connect to that MX and check whether the sender address is even accepted.
Three out of four matched in my case. The third one didn't, and that was enough.
Speaking of callouts — a detail that cost me a separate evening: the probe@ address the probe uses as its sender must be accepted by my own mail provider, otherwise remote servers reject the probe because of a non-existent sender. With Cloudflare Email Routing this is solved by a routing rule with the Drop action: the address is accepted (the callout sees 250), and the message is discarded after acceptance.
The HELO fix took a minute: one variable in the config, sslip.io → mail.proofmailapi.com. Now PTR, the A record, and HELO are one name.
The bureaucratic finale of the ticket
I replied to the ticket: cause removed, HELO ↔ rDNS match confirmed. Forty minutes later the ticket was rejected — not on the merits, but on authorization checks. Spamhaus requires provable ownership linking the requester to the IP, and out of three conditions I passed one:
condition me
rDNS resolves publicly and matches the mail domain yes
the IP's WHOIS belongs to the requester no — I rent a VPS, WHOIS shows the hosting provider
the request originates from the IP being delisted no — my mail physically leaves through my mail provider's infrastructure
The last two are fundamentally unfixable for a rented VPS with forwarded mail. Looks like a dead end — but the same rejection letter contained the most valuable sentence of the entire story:
Listings expire a few days after the last detection.
Meaning: if the cause is truly gone, there are no new detections — and the record ages out on its own. The ticket was an accelerator, not a necessity. So the plan became: don't file a third time. Wait and measure.
The finale: the listing expired by itself
Three days after the HELO fix, the record was gone — with no action on my part, exactly as the hypothesis predicted. iCloud started talking immediately (verified with a live probe from production).
And the bonus that paid for the whole investigation: once the big providers unblocked me, I could finally measure what the listing had been hiding — and discovered that Microsoft answers RCPT non-deterministically: the same non-existent address on outlook.com returns 250 or 550 depending on which backend behind the load balancer you hit, roughly 1 in 5. That's a separate story with separate consequences for anyone who trusts a single SMTP answer from Microsoft — if there's interest, I'll write it up.
Lessons
Moving from staging to production is a checklist, not a mood. PTR, SPF, HELO, MX, the callout address. One missed item out of five cost me a listing in the world's most influential blocklist.
Don't treat a hypothesis — test it. My first model of the cause was logical, elegant, and wrong. I kept the quota, but on its own merits, not as "the Spamhaus cure".
A blocklist self-check must distinguish three states: "clean", "listed", "refused to answer me". Two common mistakes collapse the last state into the first.
Read rejections to the end. The most valuable sentence of the story was in the letter that turned me down.
You may never send email — but to the rest of the internet you ARE a mail server. SMTP has no separate reputation track for "those who only ask".
The verifier from this story runs as an API — Proofmail. Its defining trait was born in investigations exactly like this one: when a mailbox cannot be confirmed, it says "I don't know" instead of guessing.
Top comments (0)