In this post I would like to explain the usage of SPF (Sender Policy Framework), which could be used to prevent spam. SPF will use the DNS system to tell the receiving mail server, whether the sending server is allowed to send mails for this domain. As this is the basic concept, I will explain it in more detail in the first section of this post. The last section will explain, how to use SPF in an postfix environment.

What is SPF

SPF or Sender Policy Framework, can help to prevent spam, as every domain owner can specify which IP is allowed to send mails for the domain. The first step, is to create a DNS TXT record, which will publish a list of IP’s which are allowed to send mails for your domain.

This record has to start with the SPF version:


After the version statement, you will need one or multiple statements. Those statements can have one of the following qualifiers:

  • “+” The following host is allowed. This is default and assumed if you did not set a qualifier in font of a statement.
  • “?” The following host should be treated as neutral.
  • “~” This indicates a softfail, which means, the other side should be treated as failed but the mail should be accepted.
  • “-” This indicates a fail.

After the qualifier, you have to specify one of eight mechanisms:

  • “all” which will match everything and should be at the end of every entry, like a default rule when working with ACL’s
  • “A” The receiver will check the “A” or “AAAA” record. If this matches the senders address, this statement applies.
  • “IPv4” If the sender matches the IP address or IP range this statement applies.
  • “IPv6” If the sender matches the IPv6 address or IPv6 range this statement applies.
  • “MX” If the MX record resolves to the senders IP, this statement matches.
  • “PTR” If the PTR record points to a domain name within the given domain and this domain resolves to the senders IP, this statement matches.
  • “exists” If the domain name exists and can be resolved, no matter to which IP, the Statement matches.
  • “include” The includes statement points to another SPF entry and if this one matches, the statement matches.

It is also possible to redirect the SPF request to another domain with the “redirect” statement.

This is everything you need to know. There are some SPF generators in the web, which are useful but the creation of such a record is very easy. The one for my mail server looks like this:

host -t TXT descriptive text "v=spf1 ip4: -all"

I will only allow mails coming from my server. If the mail is coming from a different server, the receiver should reject this mail.

Here is an example for

host -t TXT descriptive text "v=spf1"

This one redirects to “”, which looks like this:

host -t TXT descriptive text "v=spf1 ~all"

This entry hast some “include” statements and the “~all” statement which will tell the other side to do a softfail if the statements before did not match.

host -t TXT descriptive text "v=spf1 ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ip4: ~all"

This is the entry for one of the “include” statements. I think you now got the logic behind it.

How to use SPF with Postfix

To allow postfix to check the SPF record of incoming mails you need to have postfix-policyd-spf-python. You can install it by using this command on debian:

apt-get install postfix-policyd-spf-python

Afterwards you need to configure the daemon to work as required. Open the following file:

vi /etc/postfix-policyd-spf-python/policyd-spf.conf

This file should look like this:

debugLevel = 1
defaultSeedOnly = 1

HELO_reject = SPF_Not_Pass
Mail_From_reject = Fail

PermError_reject = False
TempError_Defer = False

skip_addresses =,::ffff:,::1


  • debugLevel is the log level. 1 means that normal errors and SPF results are logged. You can increase the level to 5 or disable logging with 0
  • defaultSeedOnly can set the SPF daemon into a testing mode. The daemon will check all mails and mark them but will not reject them. 0 will enable the testing mode.
  • HELO_reject has different options. The “SPF_Not_Pass” option will reject all mails where the SPF verification for the HELO/EHLO on the mail server fails. This useful to save resources, as you can reject the mail before the mail is transferred. Other options are:
    • “Softfail” which will reject the mail on softfail or fail of the SPF Verification
    • “Fail” reject only on fail.
    • “False” will never reject a mail, but will insert the header. Useful for later checks.
    • “No_Check” will do no checks.
    • “Null” will only reject fail or null sender.
  • Mail_From_reject allows to use the mail from header for checks. “Fail” will reject mails where no statement in the SPF record could be matched.
  • PermError_reject will allow you to decide, what happens with broken SPF records. “False” will handle them as if there is no SPF record. “True” will reject them.
  • TempError_Defer allow you to decide what should happen to temporary errors. “True” will reject them and “False” will will handle them as if there is no SPF record.
  • skip_addresses will allow you to enter a list of IP’s which are not checked.

Save the file and open the main postfix configuration file:

vi /etc/postfix/

Add this line to the file:

policy-spf_time_limit = 3600s

This will set the timeout for the SPF daemon. You also have to add an entry to your recipient restrictions:

check_policy_service unix:private/policy-spf

Enter the line after “reject_unauth_destination” and before the blacklist checks.

Save the file and open the postfix service file:

vi /etc/postfix/

You have to add this line at the bottom of the file:

policy-spf unix - n n - - spawn
 user=nobody argv=/usr/bin/policyd-spf

Save the file and restart postfix.

If it is working, you should see entries like this in the logs:

Aug 24 09:16:13 mail postfix/smtpd[32238]: NOQUEUE: reject: RCPT from[]: 550 5.7.1 <[email protected]>: Recipient address rejected: Message rejected due to: SPF fail - not authorized. Please see;id=[email protected];ip=;r=[email protected]; from=<[email protected]> to=<[email protected]> proto=ESMTP helo=<>

If you have any questions, regarding this post or if you would like provide feedback, please use the comment function below.