MAILCRAFT
Home Features Pricing About Blog Contact Log in Get Started →
Email deliverability

Reading a Bounce Report: Hard, Soft and the Ones That Lie

Reading a Bounce Report: Hard, Soft and the Ones That Lie

Every campaign leaves a bounce report behind, and most people read it wrong. They open it, glance at the percentage, close it. Done. Which is a shame, because the bounce report is the most honest diagnostic you get from a send - it contains words written by the receiving server about your mail. We run our own sending servers at MailCraft, so I read these logs daily. Here’s how we read them, what the categories actually mean, and which rejections quietly lie to you.

What a Bounce Report Actually Tells You

A bounce report is a log of refusals. It’s not a list of bad addresses, though everyone treats it as one. Every row has two layers: the SMTP response your sending server received, and the classification your platform stuck on it afterwards.

Those two are not equal. The SMTP string is evidence. The classification is a guess - software matching patterns against text. Good platforms guess well. None guess perfectly, because receiving servers phrase rejections however they feel like on the day.

So insist on access to the raw response, not just the label. If your tool shows you “Hard bounce” and nothing else, you’re reading someone else’s interpretation of your data.

From running our own fleet: the same rejection wording carries different weight at different providers. One large filter uses a permanent code for a temporary condition. Another buries a reputation block inside what looks like a mailbox error. Context comes from knowing who rejected you, not just what code came back.

Hard Bounces: Permanent, and Usually Honest

Hard bounces arrive as 5xx codes, most often 550 or 551. The plain reading is correct most of the time: the address doesn’t exist, or the domain refuses mail entirely.

The causes are boring. Someone fat-fingered their address at signup. An employee left and IT deleted the mailbox. A domain expired, or its MX records vanished during a migration nobody documented.

Handling is equally boring. Suppress on the first hard bounce. No retry, no second chance, no “maybe it was a glitch”. Retrying a confirmed non-existent address teaches the receiving server that you don’t check your list.

Tip: if the same address hard bounces across two campaigns, your suppression list is broken. Fix that before blaming data quality.

A hard bounce spike on an old segment usually means list age, not bad collection. Addresses decay. People change jobs. Most of these are catchable before you send, if you check whether an address exists without mailing it first.

Soft Bounces: Temporary, and Worth Retrying Carefully

Soft bounces come back as 4xx: mailbox full, server unavailable, message too large, temporary local error. The receiving side is saying “not now”, not “never”.

Greylisting belongs here by design. The server rejects an unknown sender once, expecting a legitimate system to try again. Usually clears on the second attempt.

Retry timing matters more than retry count. Hammering a server minutes apart reads as automation. Waiting too long misses the window when the message still made sense to send.

  1. First retry after roughly fifteen minutes, enough to clear greylisting.
  2. Backoff on each subsequent attempt, spacing them further apart.
  3. Cutoff after a fixed window, typically a day or two, then stop.
  4. Suppression once the same address soft bounces across several campaigns in a row.

A soft bounce that repeats forever is a hard bounce wearing better manners.

The Bounces That Lie

Here’s where reports mislead. Some 5xx codes say nothing about the recipient at all. They’re about you.

Reputation blocks dress themselves up as user errors. You get a 550 carrying a policy string, a blocklist reference, or a URL pointing at the provider’s postmaster page. The address is fine. Your sending IP or domain is not, and the fix runs through checking the DNSBL lists and working through delisting.

Catch-all domains cause the opposite problem. They accept everything, then bin it silently. No bounce, no complaint, no delivery. Your report looks clean while messages evaporate.

Rate limiting is another distortion. Certain providers return a permanent code when all they want is for you to slow down. Your platform files it as a hard bounce and suppresses a perfectly valid address. Great.

Then there are anti-spam rejections referencing SPF, DKIM, DMARC or a missing PTR record. Those describe a configuration fault, not a list fault.

Our working rule: one 5xx repeating across many recipients at a single provider on a single day is a sender problem.

Reading the SMTP Response Line by Line

An SMTP rejection has three parts: the numeric code, the enhanced status code, and free text from the receiving server. Most people read the first and ignore the third.

Reverse that habit. The numeric code gives you a category. The free text gives you the reason.

  • Policy language (“not permitted”, “rejected due to policy”) - a decision about you, not the mailbox.
  • Blocklist reference or postmaster URL - reputation, needs delisting work.
  • SPF, DKIM, DMARC, PTR mentions - authentication or DNS, fixable today.
  • “User unknown”, “no such user” - genuine mailbox state, suppress.
  • “Too many connections”, “try again later” - rate limit, throttle and resume.

And check your Return-Path domain while you’re in there. Bounces return to it. A misaligned Return-Path breaks DMARC alignment and quietly breaks bounce collection at the same time.

Tip: store the full response text. Truncating it to “bounce” destroys your only diagnostic.

What We Do When a Report Turns Ugly

First move: pull the affected segment out of rotation and isolate the provider. Change nothing else yet. You can’t measure a fix while three variables move.

Then verify authentication before assuming a blocklist. SPF record present and correct. DKIM signature on the domain actually doing the sending, not a leftover from an old setup. DMARC policy published. PTR record on the sending IP resolving properly. Configuration faults are cheaper to fix than reputation faults, so rule them out first.

Warmup works as both cure and prevention. Drop volume to the affected provider, rebuild sending history gradually, let the pattern normalise. Our own fleet of sending servers exists partly so we can throttle one provider without touching another.

For delisting: read the provider’s stated reason, fix it, then request review. Requesting review first just wastes the request.

Honest limitation: nobody can promise a blocklist exit date. Not us, not anyone.

Bounces, Consent and the Law

A high bounce rate is often a consent problem in technical costume. Bought lists bounce, because nobody in them agreed to hear from you and half the records are stale or made up.

In Poland this isn’t only a deliverability question. RODO governs the personal data, and Prawo komunikacji elektronicznej, artykuł 398, requires consent for commercial email. B2B included. A company address belonging to a named person is still covered.

Double opt-in solves two problems in one step. It catches typos before they enter your list, and it produces a timestamped record of consent you can actually show someone.

Suppression lists are legal artefacts as well as deliverability tools. They prove you stopped sending when asked.

What we log: consent source, timestamp, confirmation. What we refuse: scraped addresses, imported lists without provenance. The recurring questions about how we handle this are collected in our answers to the questions clients ask most often.

Summary: Treat the Report as a Signal, Not a Verdict

Hard bounces clean your list. Soft bounces test your patience. Policy rejections test your setup. Three different problems arrive through one report, and reading the label alone flattens them into one.

The habit worth building is small: read the free text before the classification. Thirty seconds of reading beats an hour of guessing. The rest of it is ordinary deliverability practice, applied consistently.

Three checks after every campaign:

  • Bounce rate broken down by provider, not as a single average.
  • The response text of your most frequent rejection.
  • Suppression list growth since the previous send.

On our roadmap sits automated classification that surfaces sender-side rejections separately from recipient-side ones. Not built yet. Until it ships, we read the text ourselves, and I’d suggest you do the same.