$ hygiene
Hard bounce vs soft bounce: what is the difference?
A hard bounce is a permanent rejection — the address does not exist. A soft bounce is temporary, like a full mailbox, and is worth retrying for a while.
A bounce is a receiving mail server refusing a message. The distinction that matters is whether it will ever accept it.
- A hard bounce is permanent. The address does not exist, the domain does not exist, or the server has decided it will not take mail from you. Retrying changes nothing.
- A soft bounce is temporary. The mailbox is full, the server is overloaded, greylisting is in effect. Retrying later usually works.
The difference is visible in the SMTP response code: 5xx is permanent, 4xx is temporary.
Reading the response
550 5.1.1 The email account that you tried to reach does not exist
452 4.2.2 The email account that you tried to reach is over quota
421 4.7.0 Try again later, closing connection
550 5.7.1 Message rejected due to local policy
Two codes, both meaningful:
- The first digit is the class.
5permanent,4temporary,2accepted. - The enhanced status code (
5.1.1,4.2.2) narrows it down:x.1.1bad mailbox,x.1.2bad domain,x.2.2mailbox full,x.7.xpolicy or authentication.
5.7.x is the one to read carefully. It is a permanent rejection on policy grounds — authentication failed, reputation is poor, content was refused — and unlike 5.1.1 it is usually about you rather than about the address. The address is fine. Something else needs fixing.
What to do with each
Hard bounce. Stop sending to that address, permanently and automatically. Add it to a suppression list at the moment it happens rather than in a weekly cleanup. Repeatedly mailing addresses that have permanently rejected you is one of the strongest negative reputation signals there is, and it is nearly always a retry loop rather than a decision anyone made.
Soft bounce. Retry with increasing backoff for a bounded period — a few hours to a few days is typical. Then stop. An address that has been “temporarily” full for a week is functionally dead, and most senders convert a soft bounce to a hard one after a threshold of consecutive failures.
Bounces in transactional mail
For transactional email a bounce means something more specific than it does for a campaign: someone is waiting for this message and it did not arrive. A hard bounce on a password reset is not a list-hygiene event, it is a person who cannot log in, and the useful response is usually in the product rather than in the mail system — prompt them for a different address, or show the failure in the UI instead of a silent success.
It is also worth distinguishing where the failure happened, because the fixes are different:
- Rejected before the network. Your own system refused to send — the address was on a suppression list, the domain was not verified, the payload was invalid. Nothing was transmitted.
- Rejected by the receiving server. The message went out and was refused. The response text is theirs, and it is the thing to read.
An event log that flattens both into “bounced” hides the one piece of information that tells you whose problem it is.
Asynchronous bounces
Not every rejection happens during the SMTP conversation. A server can accept a message with a 250 and then decide against it, delivering a separate bounce message to the Return-Path address minutes or hours later.
This is why a 250 at send time — or a 202 from an API — means accepted for delivery, not delivered. Anything that reports delivery success at the moment of sending is reporting the wrong event, and any system you build on that assumption will overstate how much mail arrived.
A rate worth watching
Bounce rate on transactional mail should be very low, because the addresses were typed by their owners recently. A rate creeping above about 2% usually means one of three things: signup is accepting typos with no verification, addresses are being kept long after they went dead, or something upstream is generating addresses that were never real.
Pharos suppresses on hard bounce and complaint automatically, per account, and records which stage each failure happened at. The events reference lists every event name and what it means.