Engineering

Your suppression list belongs in the mailer, not the call site

17 August 2026 · 5 min read

Every "we accidentally emailed someone who unsubscribed" incident has the same root cause: the check lived in the code that sends, and one send path forgot it.

An engineer reviewing email delivery controls with a colleague

Every team that has sent one unwanted email has the same story. There is a suppression list. There is a check. And there is one code path - a console command, a re-send button, a new campaign feature - that does not call it.

The fix is structural, not procedural. Laravel dispatches a MessageSending event before every send, and a listener returning false cancels it. That is a choke point no call site can route around.

Scope matters

Not every suppression means the same thing. Someone who unsubscribed from your newsletter has not asked you to stop replying to their own support ticket. A hard bounce means the mailbox does not exist, so nothing should go there. A spam complaint means keep away entirely.

So store a scope alongside the reason, and never let a narrower suppression overwrite a wider one.

Normalise before you hash

[email protected] and [email protected] are the same mailbox. If you store the raw string, the second one sails straight past your check. Normalise per the rules the provider actually documents, then hash - and only apply dot-stripping to domains where dots really are insignificant, or you will merge two different people into one record.