Configuring and Debugging My Linode VPS with an SPF Record that Passes the Gmail Spam Filter

I have been having no end of problems with my outbound email messages sent by my Drupal sites since I switched from shared hosting to VPS hosting. A typical shared host offers a well configured SMTP server with documentation about how to configure the MX and SPF records. On the other hand, a VPS requires the administrator to install and configure the mail transfer agent, which can be surprisingly complex. Even more difficult is the fact that the VPS provider can’t really provide a one-size-fits-all set of instructions for configuring the MX and SPF records, since there are many more variables controlled by the VPS administrator. So, suffice to say that email configuration is probably the most dreaded task of most VPS administrators. In fact, many prefer to avoid the pain and suffering altogether and use a third party email provider. However, in my case, since one of my sites sends a lot of forum activity notification emails, I prefer to save on the cost of a third party email provider and do it myself. This has turned out to be a ridiculously complex trial and error process, but I think I finally found the missing key.

I won’t even begin to get into the mail transfer agent (MTA) configuration, which is Postfix in my case. In reality, sending out an email isn’t the most difficult part of the process. I was able to configure Postfix and send out emails by following any number of guides online. However, getting the popular email service providers to actually accept the message is much more complex. In particular, I was having trouble getting Gmail to receive my messages and not classify them as spam.

Debugging spam classification in Gmail – What NOT to do

The first tip when debugging problems with email delivery to Gmail is to avoid the temptation to mark messages as Not Spam. My first reaction when testing my setup and finding emails from my site classified as spam in Gmail was to simply mark the message as Not Spam, which apparently solves the problem as subsequent messages will often pass the spam filter. However, this will not make Gmail accept messages from your server sent to other users’ Gmail accounts. It is highly unlikely that most of your users, or far worse, potential users, will be motivated enough to look for your messages in the Spam folder. New signups that are expecting their account activation email to arrive in their inbox will probably check only the inbox and will most likely wait a maximum of 30 seconds for it to arrive. Even more difficult, if you mark a message from your server as Not Spam on Gmail, you will not be able to accurately evaluate the effect of any future configuration changes on Gmail spam classification. So if your first attempts at getting messages to pass the Gmail spam filter fail, just leave them in the Spam folder and keep trying. When you finally get the configuration right, your messages will arrive in the inbox irrespective of your prior attempts that ended up as spam.

Drupal 6 and the Return-Path header problem

The second tip is specific to Drupal 6. For some reason, Drupal 6 has an irritating and unresolved bug that causes it to send out messages with a Return-Path address in the header is not the same as the From address. Apparently, the PHP mail() function sets the Return-Path based on the Linux user account name of the web server. For example, in my case if messages were sent with a From address of info@example.com, the Return-Path in the header was nginx@members.linode.com. This is a sure recipe for classification as spam on Gmail and many other email providers as well. Fortunately, there is a dead-simple Drupal 6 module that fixes this dreadful bug. Many thanks to the Return-Path module, which sets the Return-Path to the same address as the Drupal site configured default address.

MX and SPF records

The most difficult part of the configuration has to do with setting the MX and SPF records. In my case, I send email from several domains that are hosted on the same VPS, and I don’t necessarily want curious users to look through the email headers and see that emails from info@lose-weight.com are sent from the hamburgers.com server. (Please not that these examples are purely fictitious. I hate weight loss products and I also hate hamburgers.) However, in order to not be classified as spam, it is important that there be an MX record that specifies the server name that sends the mail for that domain. So one option would be to buy a toplevel domain with a neutral name, such as acme-email-server.com, and then set the VPS hostname and the MX records to that same domain name. That way, messages from lose-weight.com and hamburgers.com will both have acme-email-server.com as the originating mail server in the headers, which shouldn’t offend anybody. Another cheaper option is to use the standard Linode host name. In my case, the default Linode Reverse DNS (rDNS) hostname under the “Remote Access” tab of the Linode settings is something like li222-111.members.linode.com. So under the “Mail server” field of my Linode MX record settings, I put li222-111.members.linode.com. Likewise, I made sure the hostname of my CentOS VPS was also set to li222-111. This is a nice generic name that I don’t mind users seeing in the message headers.

Finally, I tried to set the SPF record according to the tutorials and documentation. I found the SPF Wizard to be helpful in walking me through the different options available. I finally decided on the following:
v=spf1 mx a ip4:44.33.22.111 a:li222-111.members.linode.com ~all
Basically, this record states that:
mx = servers for which an MX record exists on this domain are allowed to send email for this domain.
a = the current IP address of this domain is allowed to send email for this domain.
ip4:44.33.22.111 = this IP (version 4) address is allowed to send email for this domain.
a:li222-111.members.linode.com = this server is allowed to send email for this domain.
~all = if receiving mail servers receive a message from this domain that doesn’t match this record, they should accept it but mark it as suspicious.

Some of these clauses are probably redundant, but I wanted to make sure to cover all the options. Strangely, however, even with this SPF record in place, messages continued be flagged as spam by Gmail. Finally I carefully examined the headers of the messages after they arrived in my Gmail spam folder, and discovered something interesting:
Received-SPF: softfail (google.com: domain of transitioning info@example.com does not designate 124d:3dd2::ff32:9eff:fce2:9cd6 as permitted sender) client-ip=124d:3dd2::ff32:9eff:fce2:9cd6;
And there was the problem. Unlike my previous VPS provider, my Linode is communicating with Gmail over IPv6. So, I also needed to add the IPv6 address of my server to the SPF record as a designated IP address that can send email for this domain:
v=spf1 mx a ip4:44.33.22.111 ip6:124d:3dd2::ff32:9eff:fce2:9cd6 a:li222-111.members.linode.com ~all
As soon as this change was propagated out the Linode’s DNS servers, messages magically started to arrive in my Gmail inbox and the headers now said:
Received-SPF: pass (google.com: domain of info@example.com designates 124d:3dd2::ff32:9eff:fce2:9cd6 as permitted sender) client-ip=124d:3dd2::ff32:9eff:fce2:9cd6;

“Pass”. The most beautiful 4-letter word in existence. Messages are now flowing smoothly, users are happy again, and so am I.

Did this article help you? Please let me know in the comments section below.

Tagging: 

4 Comments

Quick question about your SPF settings!

Argh, this has been bugging me for so long – amazing to find your blog post on the subject (nicely written, to boot!)

I too host a whole load of domains on one Linode. Difference is that I use Google Apps for Business as the email provider for most of those domains, but I do still want to send mail out from the server as well. I never really gave any thought as to which domain I wanted the mail to come from when sent from Postfix on my Linode. I don’t care for using my Linode as a mail server for anything other than web apps running on the server using PHP.

I use Linode as my nameserver for each domain name, because I love the Linode DNS record interface (change DNS settings on the bus from the iPhone app – love it!)

At the moment my server’s hostname is doris.mydomain.com, but the MX records for mydomain.com point to Google as I use this domain with Google email.

So. I got a little stuck at the point where you said “So under the “Mail server” field of my Linode MX record settings, I put li222-111.members.linode.com. “

Under Linode’s remote access tab, the reverse DNS target is set to “li303-40.members.linode.com”.

I like your idea of using the generic linode domain as my originating domain for email sent from my VPS… so where should I enter that MX record?

OR… should I change the above target to doris.mydomain.com then add an MX record for mydomain.com (if so, should I keep the Google ones there? I do use mydomain.com for business enquiries at the moment and would rather stick with Google Mail!)

I’ve a feeling that I don’t even need to worry about changing the hostname set in my Linode. But I’m not entirely clear on which domain I should set up the MX record and SPF records now!

Any hints much appreciated :)

Hi there,

Hi there,

Thanks for the good question. If I understand your setup correctly, you would add the Google server(s) to your Linode DNS MX record fields. Then, your SPF record would simply be something like:
v=spf1 mx

Hey

I’ve Google Apps/Gmail handling email for my domain example.com. However, on a linode vps, i also setup mailman with postfix to handle our emailing lists.

In this case, how do i setup my SPF record for mail server/vps mail.example.com to handle emailing lists for the domain lists.example.com?
Thanks

Hi there,

Hi there,

I’m not completely sure, but I think it would be approximately similar to my configuration:
v=spf1 mx a ip4:44.33.22.111 ip6:124d:3dd2::ff32:9eff:fce2:9cd6 a:li222-111.members.linode.com ~all