Graylisting works by temporarily rejecting emails from unknown senders, forcing legitimate mail servers to retry, which helps distinguish them from spam bots that typically don’t retry.
Let’s see it in action. Imagine a new sender, sender@example.com, trying to send an email to recipient@yourdomain.com.
# On your mail server (e.g., Postfix), you'd see something like this in your logs:
# Initial connection from sender@example.com
Jun 20 10:00:01 mailserver postfix/smtpd[12345]: connect from mail.example.com[192.168.1.100]
Jun 20 10:00:01 mailserver postfix/smtpd[12345]: 1234567890: client=mail.example.com[192.168.1.100]
Jun 20 10:00:01 mailserver postfix/smtpd[12345]: 1234567890: RCPT TO:<recipient@yourdomain.com>
Jun 20 10:00:01 mailserver postfix/smtpd[12345]: NOQUEUE: reject: RCPT from mail.example.com[192.168.1.100]: 450 4.7.1 <recipient@yourdomain.com>: Recipient address rejected: Policy rejection - please try again later; from=<sender@example.com> to=<recipient@yourdomain.com> proto=ESMTP helo=<mail.example.com>
# The sender's server, if legitimate, will queue this for a later retry.
# Later, the same sender tries again:
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: connect from mail.example.com[192.168.1.100]
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: client=mail.example.com[192.168.1.100]
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: RCPT TO:<recipient@yourdomain.com>
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: FROM=<sender@example.com>
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: queue-id=...
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: size=1024
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: STARTTLS=...
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: EHLO=...
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: AUTH=...
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: DATA
Jun 20 10:15:01 mailserver postfix/cleanup[9876]: ABCDEF0123: message-id=<messageid@mail.example.com>
Jun 20 10:15:01 mailserver postfix/qmgr[5432]: ABCDEF0123: from=<sender@example.com>, size=1500, client=mail.example.com[192.168.1.100]
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: accepted message
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: bytes=1500 from=<sender@example.com> to=<recipient@yourdomain.com> proto=ESMTP helo=<mail.example.com>
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: disconnect from mail.example.com[192.168.1.100]
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: 1234567890: lost connection after RCPT TO:<recipient@yourdomain.com>
Jun 20 10:15:01 mailserver postfix/smtpd[67890]: ABCDEF0123: removed
# Now the email is accepted and delivered.
Graylisting operates on a simple premise: the first time an email arrives from a sender (identified by the triplet of sender IP address, sender email address, and recipient email address), the mail server temporarily rejects it with a 4xx SMTP error code (specifically, a 450 4.7.1 is common). This tells the sending mail server, "Not now, try again later."
Legitimate mail servers are designed to handle temporary failures gracefully. They will place the email back into their outgoing queue and retry delivery after a configurable interval, typically ranging from a few minutes to an hour. Spam bots, on the other hand, are often programmed to move on to the next target immediately after a rejection, without bothering with retries.
When the legitimate sender’s server retries the delivery, the graylisting system checks its database. If it finds a record for that specific sender triplet, and the retry occurs within a certain timeframe after the initial rejection, it recognizes this as a legitimate sender and allows the email through.
The key components you’ll be configuring are:
- The Sender Triplet: The unique identifier for an email transaction. This is typically
(sender_ip, sender_address, recipient_address). Some systems might simplify this to(sender_ip, sender_address)or even just(sender_ip)for broader, though less precise, graylisting. - The Delay/Quarantine Period: How long the system remembers a rejected triplet. If a retry occurs within this period, it’s accepted.
- The Whitelist/Greylist Timeout: How long a sender triplet is considered "known" or "trusted" after its first successful delivery. Subsequent emails from this triplet within this period are often accepted immediately without a re-check.
- The Reject Code: The
4xxSMTP status code returned for the initial rejection.450 4.7.1is standard, but others can be used.
Implementation Example (using Postfix with postgrey):
-
Install
postgrey: On Debian/Ubuntu:sudo apt-get install postgreyOn RHEL/CentOS:sudo yum install postgrey -
Configure
postgrey: The default configuration is usually sufficient, but you can adjust parameters like--delay(how long to wait before accepting a retry, e.g.,300seconds) and--timeout(how long to keep a triplet whitelisted after successful delivery, e.g.,3600seconds). Edit/etc/default/postgrey(or similar) and setPOSTGREY_OPTS="--delay=300 --timeout=3600 --unix=/var/run/postgrey/postgrey.sock". -
Configure Postfix to use
postgrey: Edit/etc/postfix/main.cf:smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unlisted_recipient, # Add this line for postgrey check_policy_service unix:/var/run/postgrey/postgrey.sock, # Other restrictions follow reject_unknown_sender_domain, ...The
check_policy_servicedirective tells Postfix to query thepostgreydaemon for policy decisions. Theunix:/var/run/postgrey/postgrey.sockpart points to the socketpostgreyis listening on. -
Restart services:
sudo systemctl restart postgreysudo systemctl reload postfix
Why this works mechanically:
The check_policy_service command invokes postgrey. postgrey consults its internal database (often a Berkeley DB file like /var/lib/postgrey/data/greylist.db) to see if the (sender_ip, sender_address, recipient_address) triplet has been seen recently and passed the initial graylisting. If it’s a new triplet, postgrey responds to Postfix with a temporary failure code (4xx), causing Postfix to reject the email and postgrey to log the triplet for future reference. If it’s a known, whitelisted triplet, postgrey responds with an acceptance code (2xx), and Postfix proceeds with delivery.
The one thing most people don’t know: The effectiveness of graylisting can be significantly hampered by parallel SMTP connections. If a sender’s server attempts to send multiple emails to different recipients on your server simultaneously, and each connection goes through a different IP address (common in load-balanced or clustered mail environments), the sender IP triplet will change for each connection. This can trick the graylisting system into thinking it’s seeing entirely new, distinct senders, forcing each one to be graylisted and delaying mail unnecessarily. Some advanced graylisting solutions try to mitigate this by using a broader identification key, like just the sender domain, or by correlating connections from the same originating mail cluster.
The next hurdle you’ll face is handling the initial delay for legitimate senders, especially for critical communications or users who might not understand why their email isn’t arriving instantly.