Self-hosted email marketing with full source code. Pay once, own forever. Get AcelleMail — $74 →

Bounce Types Deep Dive: Hard, Soft, and the Edge Cases

A field guide to email bounces — hard vs soft vs unknown, real SMTP code interpretation, what AcelleMail does with each, and the bounce patterns that signal real deliverability problems.

Every email-marketing operator has a sense that bounces are bad. The deeper truth is that bounces are diagnostic — different bounce types carry different signals. A spike in hard bounces means one thing (list hygiene); a spike in soft bounces means a very different thing (sender reputation event). Reacting wrongly to a bounce surge — pruning the list when the problem is reputation, or warming the IP when the problem is data quality — burns budget and time.

This article walks the bounce taxonomy AcelleMail uses (hard, soft, unknown — see app/Model/BounceLog.php), what each means at the SMTP level, what AcelleMail does automatically with each, and the patterns that should prompt operator action.

The three AcelleMail bounce categories

AcelleMail's BounceLog model defines three constants:

public const HARD = 'hard';
public const SOFT = 'soft';
public const UNKNOWN = 'unknown';

A BounceLog row is created via recordHardBounce() or recordSoftBounce() (or record() with an explicit type) when the bounce-handler IMAP poller encounters a non-deliverable status notification, or when the synchronous SMTP exchange returns a permanent failure. The row carries:

  • tracking_log_id — points at the original send
  • customer_id — for multi-tenant isolation
  • bounce_type — one of the three constants above
  • runtime_message_id — the Message-ID the receiver saw
  • raw — the full DSN body (preserved verbatim — read it when in doubt)

Hard bounces — permanent failures

Hard bounces are the SMTP equivalent of "this address will never accept mail." AcelleMail marks them by:

  • 5xx responses during the SMTP transaction — the receiver synchronously rejects with 550, 551, 552, 553, 554 etc.
  • Async DSNs with 5.x.x in the status field — RFC 3463 enhanced codes returned by the receiving MTA via bounce email.

The semantics across the 5xx range:

SMTP code DSN code Meaning Action
550 5.1.1 User unknown Suppress permanently
550 5.1.10 Recipient address rejected (user not found in directory) Suppress permanently
550 5.7.1 Sender denied (policy block) Reputation issue — investigate
550 5.4.6 Routing loop / too many hops Misconfigured MX — check recipient's setup
552 5.2.2 Mailbox over quota Soft in spirit, hard if persistent — see edge cases
553 5.7.1 Mailbox not allowed Domain block — investigate
554 5.7.1 Refused Reputation block at edge filter

The crucial distinction: 5.1.1 (user unknown) is a list-hygiene issue. 5.7.1 (policy denied) is a reputation issue. AcelleMail records both as bounce_type = hard, but the corrective action is different. Always read the raw field of the bounce log when investigating sustained hard-bounce rates above 2 %.

AcelleMail's auto-suppression: hard bounces are added to the customer's bounce_list (a per-tenant suppression table). Future campaigns from that customer skip suppressed addresses automatically. You can't undo a suppression by re-importing the address — you must explicitly remove it from the suppression list.

Soft bounces — transient failures

Soft bounces are deferrals: "I can't accept this right now, try later." Common SMTP causes:

  • 4xx responses during the SMTP transaction (421 service not available, 450 mailbox unavailable, 451 local error).
  • Async DSNs with 4.x.x status fields — the receiving MTA queued and then deferred.

AcelleMail's worker queues a retry on a 4xx (Laravel queue retry semantics; configured in the queue connection). Retry interval grows with attempt count: 5 min → 15 min → 1 hour → 6 hours.

Common soft-bounce diagnostics:

Code Meaning Action
421 Service not available; throttling Reduce send rate — likely reputation event
421 RP-002 Microsoft "rate-protected" defer YELLOW SNDS — pause Outlook sending
421 4.7.0 [TSS04] Yahoo reputation defer Pause Yahoo sending; reduce volume
450 4.2.1 Mailbox temporarily disabled Re-attempt — auto-resolves
451 4.4.2 Local error in processing Receiver-side problem — usually transient
452 4.2.2 Mailbox over quota Sometimes promotes to hard if persistent

The soft-bounce trap is that a sender reputation event manifests as soft bounces from many recipients simultaneously. If you have a sudden surge of 421 codes, it's almost never a transient receiver-side problem — it's your IP getting throttled. The symptom looks like "deferrals," but the underlying cause is reputation. Pruning the list won't help; pausing volume + investigating reputation will.

Unknown bounces — the FBL bucket

bounce_type = unknown is what AcelleMail uses for Feedback Loop (FBL) abuse reports — when a recipient clicks "Report Spam," their ISP sends a feedback message in Abuse Reporting Format (ARF), and AcelleMail's FeedbackLoopHandler ingests it. The constants:

public const FEEDBACK_TYPES = ['abuse', 'fraud', 'virus', 'other', 'not-spam'];
public const EXCLUDED_FEEDBACK_TYPES = ['not-spam'];

The not-spam type is excluded — that's a "not spam" override from the recipient and isn't a complaint at all. Everything else gets logged as a complaint and adds to the customer's complaint rate.

FBL handling differs from hard/soft bounces:

  • Auto-action: the recipient is added to the customer's complaint_list (separate from bounce_list).
  • Threshold: Gmail tolerates ≤ 0.10 % complaint rate; Outlook ≤ 0.40 %. Above either, ISP-level reputation drops.
  • Reading the source: the raw field contains the original ARF report — Feedback-Type: abuse, Original-Mail-From:, etc. Useful for verifying the FBL is correctly configured.

What patterns mean what

Once you have a few weeks of bounce log data, the patterns tell you the story. Three distinct shapes:

Pattern A — list hygiene problem

  • Symptom: sustained hard bounce rate 3-8 %, mostly 5.1.1 user unknown.
  • Cause: list contains old addresses, typos, role addresses, single-opt-in junk.
  • Action: re-engagement campaign + prune zero-engagement subscribers. Don't pause sending.

Pattern B — reputation event

  • Symptom: surge of soft bounces (421, 4.7.0, 4.7.1) across many recipients in a short window.
  • Cause: ISP throttling or grey-listing your IP.
  • Action: pause new volume immediately; send only to engaged segment for 7 days; investigate Postmaster Tools / SNDS.

Pattern C — content / FBL spike

  • Symptom: complaint rate jumps to 0.5-2 % on a specific campaign.
  • Cause: subject line, content, or unsubscribe-link change is triggering reports.
  • Action: review the campaign, audit the unsub link, audit the engagement segment.

The pattern matters more than the absolute rate. A 2 % bounce rate with the Pattern A shape is fixable; a 0.5 % rate with the Pattern C shape is more dangerous.

AcelleMail-specific operations

Two daily checks:

# 1. Bounce-rate-by-cause for the last 24h:
cd /var/www/acellemail
php artisan tinker --execute='
$rows = \App\Model\BounceLog::where("created_at", ">=", now()->subHours(24))
    ->selectRaw("bounce_type, count(*) as n")
    ->groupBy("bounce_type")
    ->get();
foreach ($rows as $r) echo "$r->bounce_type: $r->n\n";
'

# 2. Top-10 5xx codes over the last 24h:
mysql -e "
SELECT SUBSTRING_INDEX(raw, ' ', 2) as code, COUNT(*) c
FROM bounce_logs
WHERE created_at > NOW() - INTERVAL 24 HOUR AND bounce_type = 'hard'
GROUP BY code ORDER BY c DESC LIMIT 10;
" acellemail

Anything outside of 5.1.1 user unknown and a small 5.0.0 / 5.7.1 baseline warrants a closer look.

Related reading

FAQ

Should I retry hard bounces?

No. AcelleMail suppresses hard-bounced addresses; re-sending forces another bounce, which doubles the reputation damage with no upside.

When does a soft bounce get promoted to hard?

After the configured retry budget is exhausted (default 4 retries over ~7 hours). The original soft bounce row stays as soft; a new row is logged as hard if the final retry returns 4xx-then-5xx or all retries 4xx.

What about transient 4.7.0 codes from Gmail specifically?

Gmail's 4.7.0 is "may have something to do with your reputation." It's a soft bounce by category but a reputation signal by content. Treat it like Pattern B (reputation event) regardless of frequency.

Can I bulk-delete bounce records to "reset" a list?

You can delete BounceLog rows but the suppression-list entries (bounce_list, complaint_list) are separate tables that survive bounce-log cleanup. Don't bulk-delete suppressions — re-introducing previously-bounced addresses is the fastest way to tank reputation.

More in Sending & Deliverability