Spam Filtering Architecture: Milters, Bayes, and Greylisting
Authentication proves mail is legitimate. Spam filtering proves it isn't malicious. Here's how to architect a filtering stack that protects deliverability.
Why Filtering Matters for Deliverability
Authentication ensures that legitimate mail is recognized. Spam filtering ensures that illegitimate mail is stopped. Both are necessary for deliverability — a server that forwards spam damages its IP reputation, which in turn affects the deliverability of all mail it sends.
A single spam complaint from a Gmail user can trigger reputation penalties that affect every message from that IP for days. The filtering stack is not just about protecting recipients — it is about protecting the sender's ability to deliver mail.
Milter-Based Filtering
The recommended architecture integrates a spam filtering engine (such as Rspamd or Amavis) as a Postfix milter. The milter protocol allows the filter to inspect and act on messages during the SMTP transaction, before the message is accepted into the queue.
smtpd_milters = inet:127.0.0.1:11332
non_smtpd_milters = inet:127.0.0.1:11332
milter_default_action = accept
This is more efficient than post-queue filtering because rejected messages never consume queue resources. The sender receives an immediate rejection during the SMTP session, which is the correct behavior — it tells the sending server that the message was not accepted, preventing bounce generation.
The milter_default_action = accept setting is a deliberate choice: if the filtering engine is temporarily unavailable, mail continues to flow rather than being rejected. This prioritizes availability over filtering — the correct trade-off for infrastructure where delayed mail is worse than unfiltered mail.
Bayesian Classification
A Bayesian spam classifier learns from every message it processes. Over time, it builds a statistical model of what spam and ham look like for the specific mail flows it handles. This is more effective than static rule sets because it adapts to the actual spam targeting the server.
The classifier state should be stored in a persistent backend (Redis is the standard choice) so that learned intelligence survives service restarts. Auto-learning should be enabled: messages with very high spam scores are automatically classified as spam, and messages with very low scores are classified as ham.
This creates a feedback loop where the classifier improves with every message that flows through the system, without requiring manual training.
Greylisting: The Behavioral Filter
Greylisting temporarily rejects the first message from an unknown sender/recipient/IP triplet with a 450 (temporary failure) response. Legitimate mail servers comply with SMTP standards and retry after a delay. Spam bots, which are optimized for volume and speed, typically do not retry.
Testing has shown that greylisting can increase spam detection rates from 99% to 99.9% when combined with content-based filtering (NetSec News — Email Protection). The improvement is small in percentage terms but significant in absolute numbers — at scale, the difference between 99% and 99.9% is the difference between 10 spam messages per thousand and 1.
The key is to apply greylisting selectively:
- Only greylist messages that already have a non-trivial spam score (above 1.0)
- Exempt authenticated users and trusted networks
- Keep the delay short (5 minutes is typical)
- Whitelist known triplets for at least 7 days to avoid re-greylisting regular correspondents
Greylisting is not a replacement for content-based filtering. It is an additional behavioral layer that catches the spam that content filters miss — particularly from botnets that send from fresh IPs with no reputation history.
Domain-Level Blocklists
A database-backed sender blocklist that supports both exact addresses and wildcard patterns (e.g., *@spam-factory.xyz) provides fine-grained control over which senders are rejected.
The blocklist should be checked early in the recipient restriction chain — before the message body is transmitted — to minimize resource consumption. A blocked sender should be rejected at the SMTP level, not after the message has been received and processed.
The Layered Effect
Each filtering layer catches a different category of spam:
- Blocklists catch known-bad senders immediately
- Bayesian classification catches content-based spam patterns
- Greylisting catches hit-and-run botnets that don't retry
- Rate limiting catches volume-based attacks (covered in the next article)
No single layer is sufficient. Together, they provide defense in depth where each layer compensates for the blind spots of the others.