Understanding Email Bounces
Hard bounces, soft bounces, DSN format, enhanced status codes — and how to handle them programmatically without destroying your sender reputation.
Two Ways Bounces Happen
Email delivery can fail at two distinct points, and the mechanism differs for each:
Synchronous rejection (during the SMTP session)
The receiving server rejects the message in real time, during the SMTP conversation. You get the error immediately as an SMTP response code:
550 5.1.1 The email account that you tried to reach does not exist.
This is the cleanest type of bounce. Your sending server knows immediately that delivery failed and can act on it.
Asynchronous bounce (DSN after acceptance)
The receiving server accepts the message (250 OK at DATA), but later discovers it cannot deliver — the mailbox is over quota, content filtering rejected it, or an internal relay failed. The server generates a Delivery Status Notification (DSN) and sends it back to the envelope sender (MAIL FROM address).
This is why the MAIL FROM address matters. It is the address that receives bounce notifications. For transactional email, this is often a dedicated bounce-processing address like bounces+tag@yourdomain.com.
Hard Bounces vs. Soft Bounces
| Hard Bounce | Soft Bounce | |
|---|---|---|
| SMTP code | 5xx (permanent) | 4xx (temporary) |
| Meaning | Delivery will never succeed | Delivery may succeed later |
| Examples | User unknown, domain doesn't exist, address rejected by policy | Mailbox full, server busy, greylisting, rate limiting |
| Action | Remove from list immediately | Retry with backoff; suppress after repeated failures |
The distinction is critical. Continuing to send to hard-bounce addresses damages your sender reputation. Mailbox providers track your bounce rate — if it exceeds roughly 2%, your deliverability will suffer across the board.
Enhanced Status Codes
Modern SMTP servers use enhanced status codes (RFC 3463, registry in RFC 5248) that provide much more detail than the basic three-digit code. The format is class.subject.detail:
- Class: 2 = success, 4 = temporary failure, 5 = permanent failure
- Subject: The category of the problem
- Detail: The specific issue
Subject categories
| Subject | Category | Description |
|---|---|---|
| X.0.X | Other/undefined | Catch-all for uncategorized status |
| X.1.X | Addressing | Problems with the mailbox or address |
| X.2.X | Mailbox | Mailbox status (full, disabled, etc.) |
| X.3.X | Mail system | Destination mail system issues |
| X.4.X | Network/routing | Network or routing failures |
| X.5.X | Mail delivery protocol | SMTP protocol issues |
| X.6.X | Message content | Content or media problems |
| X.7.X | Security/policy | Security or policy violations |
Status codes you will see most
| Code | Meaning | Action |
|---|---|---|
5.1.0 |
Other address status | Hard bounce — remove |
5.1.1 |
Bad destination mailbox address | Hard bounce — the mailbox does not exist. Remove immediately. |
5.1.2 |
Bad destination system address | Hard bounce — the domain itself is invalid |
5.1.3 |
Bad destination mailbox address syntax | Hard bounce — malformed address |
4.2.1 / 5.2.1
|
Mailbox disabled | Account suspended or deactivated |
4.2.2 / 5.2.2
|
Mailbox full | Over quota. Temporary if 4xx, permanent if 5xx. |
5.2.3 |
Message too large | Reduce message size or attachments |
4.4.1 |
Connection timed out | Retry later |
4.4.2 |
Connection dropped | Retry later |
5.7.1 |
Delivery not authorized | Policy rejection — message or sender is blocked |
5.7.26 |
DMARC failure | Fix your authentication records (SPF, DKIM, DMARC) |
4.7.1 |
Greylisting / rate limiting | Retry after delay |
DSN Format: What a Bounce Message Looks Like
When a bounce is generated asynchronously, it arrives as a structured message defined by RFC 3464, wrapped in RFC 3462's multipart/report MIME type. A DSN has three parts:
boundary="boundary42"
--boundary42
Content-Type: text/plain
Your message to bob@example.com could not be delivered.
The recipient's mailbox does not exist.
--boundary42
Content-Type: message/delivery-status
Reporting-MTA: dns; mx1.example.com
Arrival-Date: Tue, 11 Mar 2026 14:00:00 +0000
Final-Recipient: rfc822; bob@example.com
Action: failed
Status: 5.1.1
Diagnostic-Code: smtp; 550 5.1.1 User unknown
Last-Attempt-Date: Tue, 11 Mar 2026 14:00:05 +0000
--boundary42
Content-Type: message/rfc822
[Original message headers included here]
--boundary42--
The three MIME parts:
- text/plain — Human-readable explanation
- message/delivery-status — Machine-readable structured data with the status code, recipient, and diagnostic info
- message/rfc822 — The original message headers (or full message), so you can identify which message bounced
The key fields for programmatic processing:
- Final-Recipient: The address that failed
-
Action:
failed(permanent),delayed(temporary),delivered,relayed, orexpanded -
Status: The enhanced status code (e.g.,
5.1.1) - Diagnostic-Code: The raw SMTP response
Handling Bounces Programmatically
Use VERP for reliable identification
VERP (Variable Envelope Return Path) encodes the recipient address into the MAIL FROM, so when a bounce comes back, you can identify exactly which recipient bounced without parsing the DSN body:
When bob@example.com bounces, the DSN is delivered to bounces+bob=example.com@yourdomain.com. You parse the local part to extract the original recipient. This works even when the DSN is malformed or non-standard.
Processing logic
A reliable bounce processor follows this logic:
-
Parse the DSN — Extract the
message/delivery-statuspart. Read theStatus:field. -
Classify the bounce:
- Status starts with
5.→ Hard bounce. Suppress the address. - Status starts with
4.→ Soft bounce. Increment a counter.
- Status starts with
-
Apply suppression rules:
- 1 hard bounce → suppress immediately
- N soft bounces within a window (e.g., 3 within 7 days) → suppress as hard
- Identify via VERP if DSN parsing fails (many servers send non-standard bounces)
- Record everything — Log the status code, diagnostic, timestamp, and original message ID for debugging
Watch for false hard bounces
Some servers incorrectly return 5xx for temporary conditions (like rate limiting). If you see 5.7.1 with a message about "too many connections" or "try again later," treat it as a soft bounce despite the 5xx class. Use the diagnostic text as a secondary signal.
Bounce Rate and Reputation
Mailbox providers monitor your bounce rate as a signal of list hygiene:
- < 2% bounce rate — Acceptable
- 2-5% — Warning zone. You will see throttling and increased spam folder placement.
- > 5% — Your IP and domain reputation are actively degrading. Some providers will block you.
For transactional email (password resets, receipts), bounce rates should be well under 1%. High bounce rates on transactional mail suggest your application is accepting invalid addresses at signup. Add email validation before ever sending.
What Can Go Wrong
Ignoring bounces entirely
The most common mistake. If you keep sending to addresses that hard-bounce, mailbox providers will throttle and eventually block your sending IP. Your deliverability will degrade for all recipients, not just the invalid ones.
Non-standard bounce formats
Not all MTAs generate RFC-compliant DSNs. Some send plain text bounces with no message/delivery-status part. Others include the status code only in the human-readable text. Your parser needs to handle these gracefully — which is why VERP is so valuable as a fallback identification method.
Backscatter
When a server accepts a message from a spoofed sender and then generates a bounce, that bounce goes to the forged MAIL FROM address. The innocent domain owner receives bounce messages for mail they never sent. This is called backscatter and is a form of abuse. Modern servers should reject during the SMTP session (synchronously) rather than accepting and bouncing later.
Bounce loops
If your bounce-processing address itself bounces (because of a misconfiguration), it can create a loop. SMTP prevents infinite loops by requiring that bounce messages (those with MAIL FROM:<>) are never themselves bounced — they are silently discarded if delivery fails.
Suppression list management
Once you suppress an address, you need a process for re-enabling it. People fix their mailbox (clear quota, re-enable account). A common pattern: allow recipients to re-validate by successfully receiving a confirmation email after a cooling-off period (30-90 days).
Key Takeaways
- Hard bounces (5xx) are permanent — stop sending immediately. Soft bounces (4xx) are temporary — retry with backoff.
- Enhanced status codes (
X.Y.Z) give you the specific reason. Learn the common ones. - DSNs have a structured
message/delivery-statuspart for machine parsing. Use it. - VERP encodes the recipient in the return path for reliable bounce identification.
- Keep your bounce rate under 2%. For transactional email, under 1%.
- Validate email addresses at the point of collection, before you ever send.