Effective Bounce Handling Strategies
How to handle email bounces gracefully and maintain a healthy sender reputation.
Sarah Chen
Deliverability Expert
Bounces are inevitable in email, but how you handle them determines your sender reputation and long-term deliverability. Here's a comprehensive guide to bounce management.
Understanding Bounce Types
Hard Bounces
Permanent delivery failures that won't resolve with retries:
- **Invalid address**: The email address doesn't exist
- **Domain doesn't exist**: The recipient's domain is invalid
- **Blocked**: The recipient has blocked your domain
Action: Remove immediately from your list. Never retry.
Soft Bounces
Temporary delivery failures that might succeed later:
- **Mailbox full**: Recipient's inbox is over quota
- **Server unavailable**: Temporary outage
- **Message too large**: Email exceeds size limits
- **Rate limited**: Too many emails sent too quickly
Action: Retry with exponential backoff. Remove after 3-5 consecutive soft bounces.
Implementing Bounce Handling
Webhook-Based Processing
Postalynk sends bounce notifications via webhooks:
app.post('/webhooks/email', async (req, res) => {if (event === 'bounced') { await handleBounce(data); }
res.json({ received: true }); });
async function handleBounce(bounceData) { const { recipient, bounceType, bounceCode, messageId } = bounceData;
// Log for analysis await db.bounces.create({ email: recipient, type: bounceType, code: bounceCode, messageId, timestamp: new Date(), });
// Take action based on type if (bounceType === 'hard') { await removeFromAllLists(recipient); await notifyRelevantTeam(bounceData); } else { await trackSoftBounce(recipient); } } ```
Soft Bounce Escalation
Track soft bounces and escalate to suppression:
async function trackSoftBounce(email) {
const key = `soft-bounce:${email}`;
const count = await redis.incr(key);if (count >= 5) { // Too many soft bounces - treat as hard bounce await removeFromAllLists(email); await redis.del(key); } } ```
Building a Suppression List
What to Suppress
Your suppression list should include:
- **Hard bounces**: Immediate suppression
- **Spam complaints**: Immediate suppression
- **Unsubscribes**: Immediate suppression
- **Repeated soft bounces**: After threshold reached
Suppression List Structure
CREATE TABLE email_suppressions (
email VARCHAR(255) PRIMARY KEY,
reason ENUM('hard_bounce', 'soft_bounce', 'spam', 'unsubscribe'),
source VARCHAR(100), -- Which list/campaign
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Check Before Every Send
async function sendEmail(params) {
// Check suppression list first
const suppressed = await db.emailSuppressions.findUnique({
where: { email: params.to },if (suppressed) { logger.info('Email suppressed', { email: params.to, reason: suppressed.reason }); return { suppressed: true, reason: suppressed.reason }; }
return postalynk.send(params); } ```
Analyzing Bounce Patterns
Monitor Bounce Rates
Track bounces by:
- **Domain**: Identify problematic recipient domains
- **Campaign**: Some content triggers more bounces
- **List age**: Older lists have more bounces
- **Source**: Where did these emails come from?
Healthy Benchmarks
| Metric | Good | Concerning | Critical |
|---|---|---|---|
| Hard bounce rate | <0.5% | 0.5-2% | >2% |
| Soft bounce rate | <2% | 2-5% | >5% |
| Total bounce rate | <2% | 2-5% | >5% |
Investigate Spikes
If bounces suddenly increase:
- Check for list imports (new addresses often have issues)
- Review recent DNS/infrastructure changes
- Verify you haven't been blacklisted
- Check if a major ISP is having issues
Cleaning Your Lists
Regular Hygiene
Schedule regular list cleaning:
// Monthly job to clean inactive subscribers
async function cleanInactiveSubscribers() {
const sixMonthsAgo = new Date();const inactive = await db.subscribers.findMany({ where: { lastEngagement: { lt: sixMonthsAgo }, status: 'active', }, });
// Option 1: Move to re-engagement campaign await moveToReEngagementList(inactive);
// Option 2: Suppress immediately // await suppressEmails(inactive.map(s => s.email)); } ```
Email Verification
Before importing lists, verify emails:
async function verifyAndImportList(emails) {
const results = {
valid: [],
invalid: [],
risky: [],for (const email of emails) { const verification = await emailVerifier.verify(email);
if (verification.isValid) { results.valid.push(email); } else if (verification.isRisky) { results.risky.push(email); } else { results.invalid.push(email); } }
// Only import valid emails await importSubscribers(results.valid);
return results; } ```
Recovery Strategies
After a Bounce Spike
- **Stop sending** to the affected segment
- **Analyze** the bounce data for patterns
- **Clean** the affected list
- **Verify** remaining addresses if needed
- **Resume slowly** with engaged users first
Reputation Recovery
If your bounce rate damaged your reputation:
- Reduce sending volume temporarily
- Send only to highly engaged users
- Monitor delivery metrics closely
- Gradually increase volume over weeks
Conclusion
Bounce handling isn't glamorous, but it's essential for email success. Implement proper suppression lists, monitor bounce rates, and clean your lists regularly.
Postalynk handles bounce processing automatically and provides detailed bounce analytics. Set up webhook handlers, and we'll give you real-time bounce notifications to keep your lists healthy.
Related Articles
10 Best Practices for Improving Email Deliverability
Learn how to optimize your email sending practices to ensure your messages reach the inbox, not the spam folder.
TechnicalUnderstanding SPF, DKIM, and DMARC: A Complete Guide
A comprehensive guide to email authentication protocols and why they're essential for your sending reputation.