If you have an application which sends out e-mails, you would sooner or later be facing the problem that some of your e-mail addresses become out of date. It would be nice if your system could detect this automatically and take invalid addresses out of the database.

Unfortunately, there’s no standard method for that kind of problem. One of the difficulties is to detect a bounce e-mail. Different e-mail servers each create very different types of bounce backs, so it’s not trivial to distinguish a bounce e-mail from a regular e-mail.

Given the fact that a lot of people should face this, there are surprisingly few software libraries around. My research lead to

To be honest, I haven’t tried any of these. The two inactive projects are no good choices for obvious reasons, and I neither want to use a commercial product if I can avoid it. So what’s left is Mail::DeliveryStatus::BounceParser, but well - we’re not a Perl shop and actually there’s a slightly simpler solution.

A Simple Procmail Recipe

Procmail is a mail delivery agent (MDA), i.e., on your e-mail server it delivers mail to your local mailboxes. If you run a Linux e-mail server, it’s easy to integrate it; just Google, there are tons of tutorials. It includes a DSL for filtering out e-mails (actually it’s been around much longer than the term “DSL” is being hyped). A filtering rule is called recipe in Procmail. We use the following recipe to detect bounce mails:

BOUNCE="((^Subject:( )*delivery status notification)|\
(^Subject:( )*delivery failure)|\
(^Subject:( )*delivery notification: delivery)|\
(^Subject:( )*failure (notice|delivery))|\
(^Subject:( )*invalid (e)?(-)?mail account)|\
(^Subject:( )*mail could not be delivered)|\
(^Subject:( )*mail delivery fail(ed|ure))|\
(^Subject:( )*(e)?mail delivery error)|\
(^Subject:( )*mail system error)|\
(^Subject:( )*message not delivered)|\
(^Subject:( )*message rejected)|\
(^Subject:( )*nondeliverable (e)?mail)|\
(^Subject:( )*non( )?delivery report)|\
(^Subject:( )*notice: mail delivery status)|\
(^Subject:( )*returned (e)?mail)|\
(^Subject:( )*returnmail)|\
(^Subject:( )*undeliverable mail)|\
(^Subject:( )*undeliverable message)|\
(^Subject:( )*undeliverable:)|\
(^Subject:( )*undelivered mail)|\
(^Subject:( )*warning: could not send message)|\
(^Subject:( )*warning:( )?message .* delayed)|\
(^Subject:( )*message status - undeliverable)|\
(^Subject:( )*email delivery error)|\
(^From:.*nondelivery@)|\
(^From:.*postmaster)|\
(^From:.*mail delivery (sub)?system)|\
(^From:( )*(e)?(-)?mail administrator)|\
(^From:.*mailer-daemon)|\
(^From:.*fetchmail-daemon)|\
(^From:.*admin@))"

:0:
* $ ${BOUNCE}
/var/mail/mybounce

Of course it’s not perfect, you won’t get a 100% detection rate. But it’s a simple and pragmatic approach which - at least for us - worked pretty well so far.