Email may be simple conceptually, but actually getting a message to a recipient's inbox seems to get harder every year. This is primarily caused by the arms race between mail service providers and spammers, phishers, and other bad actors. New offensive tactics lead to new defensive tactics, in the form of new protocols and rules. For the innocent server admin sending a few notifications, this results in quite a game of catch-up.
Here are a few ways to avoid the spam filter:
Mail servers use a number of blacklists to track IP addresses used by known spammers. If your emails are being consistently rejected or spam-filtered, check your server's IP address against these blacklists in case the previous owner of your IP address did some bad things. MXToolBox has a nifty tool for this.
Should you find yourself on a list, check the list's website for information on getting de-listed.
Forward-confirmed reverse DNS🔗
Your server probably has a DNS hostname (eg.
mail.example.com). That forward record is used for resolving a name to an IP address. A reverse record is one that maps an IP address back to a hostname. You can look up a forward record using
nslookup mail.example.com, and you can look up a reverse record with
nslookup 203.0.113.123. Your forward record should match the reverse record. A server at
mail.example.com shouldn't have a reverse hostname of
To fix this, update the reverse DNS record for your server's IP address to contain its correct hostname. This record is managed by whoever owns the IP address: your ISP or hosting provider.
SPF (Sender Policy Framework) uses DNS records to verify that a server sending mail with a
From: email@example.com header is actually allowed to send mail on behalf of
example.com. Access your DNS management interface, and create a TXT and an SPF record (with identical contents):
example.com. 86400 IN TXT "v=spf1 a ip4:203.0.113.123 ip6:2001:DB8::aa:bb:cc:dd ~all" example.com. 86400 IN SPF "v=spf1 a ip4:203.0.113.123 ip6:2001:DB8::aa:bb:cc:dd ~all"
Replace the IP addresses with your own, of course. You can include multiple
ip6 sections if you have more than one server. For an SPF syntax reference, see this helpful page. Remember that DNS records are cacheable, so an SPF update can take a day or longer to reach some mail servers. If you aren't sure about your changes, replace that
86400 (cache time-to-live in seconds) with a smaller value like 60 (one minute) so you can revert them quickly.
DKIM Records (and headers)🔗
DKIM (DomainKeys Identified Mail) uses cryptographic keys to prove that your server is authorized to send mail for your domain (the same reason we have SPF). You'll need to generate a private/public key pair for your mail server, configure your mail server to sign outgoing messages with the private key, and share the public key with the world using another DNS record.
First, generate the keypair. Don't make the key smaller than 1024 bits (otherwise Gmail, among others, won't trust it). Here's how you might do this on Linux:
openssl genrsa -out private.pem 1024 -outform PEM openssl rsa -in private.pem -out public.pem -pubout -outform PEM
After generating a keypair, configure your mail server to sign outgoing messages using the private key. Debian-administration.org has an excellent tutorial on doing this with exim4.
Finally, announce the public key with a DNS record. Here's the format:
your_key_label._domainkey.example.com. 86400 IN TXT "v=DKIM1; k=rsa; p=PUB_KEY_GOES_HERE"
your_key_label with whatever key label you configured in your mail software, and replace
PUB_KEY_GOES_HERE with the contents of your public key (don't include the "BEGIN" and "END" lines, and remove any line breaks).
Test the Records🔗
Its easy to check your work with SPF and DKIM: send a message to the Port25 Solutions Email Verification test tool and you will receive an automated report moments later. For a second opinion, send a test message to a Gmail account. Find the message, and look at the headers. The
Authentication-Results header will show the result of Google's SPF and DKIM checks. Here's what you want to see:
Authentication-Results: mx.google.com; spf=pass (google.com: domain of firstname.lastname@example.org designates 203.0.113.123 as permitted sender) email@example.com; dkim=pass firstname.lastname@example.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=example.com; s=your_key_label; h=Date:Message-Id:To:From:Subject; bh=vaDhn7R9d+nyJO//MTxaFLQMaXlBbkuQTL3obpK83gM=; b=c0d7YN4+xPMxUoFccfXYnwUSh3cvRwzyQ3K68DIogrHYTNej5TzZ4w4aiY5N92wZyzd3tgjGQwV/sQbfxH/EQpF+leg6HHPVsv1pYR78/4hjEBJgrX6b/E2ezw3ICTOglC//Cyz7RLdniSqavXQr3elIJI4giliNU0Eu6Qb3mOo=;
DMARC, configured by yet another DNS entry, gives you two capabilities:
- Tell mail servers how strictly they should treat messages claiming to originate from your domain.
- Be notified anytime an email from your domain fails authentication checks.
Here's a DMARC record which says "reject any mail claiming to be from my domain if it fails authentication checks. Send daily failure reports to email@example.com":
_dmarc.example.com. 86400 IN TXT "v=DMARC1; p=reject; rua=mailto:firstname.lastname@example.org"
Check the authoritative source for more details.
Working "From:" address and postmaster🔗
The address in your
From: header should be able to receive mail (somewhere, not necessarily on this server) and should be monitored for bounces and other mail-delivery-related notifications. A email@example.com account should also exist, and should also be monitored.
Working MX records🔗
This may be obvious from the previous section, but you should have working MX records enabling the delivery of incoming mail. You do not need to have an MX record for every server that sends mail, just the ones which receive it. I use Google Apps for my personal domain, so my MX records point to their servers (even though I send messages from plenty of other places).
These steps have been sufficient to get consistent mail delivery for my websites and those of my clients. If I've missed something, please drop a line below or let me know directly.