Next: Appendix 3 Up: Network Liaison's Handbook Previous: Appendix: IP Address Registry (IPR)

Appendix: Sender Policy Framework (SPF)

Sender Policy Framework (SPF) is a DNS-based mechanism for describing which hosts are allowed to send mail for a domain, in the form of a TXT record. It is defined by RFC 7208, and is recommended to be configured for any departmental mail servers.

We currently have no automated process with which to add TXT records to the VT DNS servers. Instead, NLs must email the hostmaster to request a manual record entry.

A full explanation of how SPF works, why you should use it, or how you should construct your own policy are beyond the scope of this document, but we provide the following to make it easier to write a basic policy.

Self-hosted (on VT campus)

For any self-run mail server, we strongly recommend configuring an SPF merely allowing the mail exchanger and soft-failing all other domains:

v=spf1 +mx ~all

Please note: it is important that your mail exchanger has reverse records for any addresses it uses to send; many domains filter based on reverse DNS. If you have users sending mail from several IP addresses, please refer to the more extensive documentation below or in the RFC to construct your records.


If you outsource your departmental mail server to Exchange, or some other service, they should provide guidance on the SPF record you should use. For Exchange, that will likely look like:

v=spf1 ~all

Summary of SPF record format

Section 5 of RFC 7208 is currently the authoritative source for the definition of SPF records, however we attempt to summarize common options with examples here, for convenience.

Statement composition

Every SPF record is stored in a TXT record. e.g.:

$ dig +short txt
"v=spf1 ip4: ip6:2001:468:c80:a102:1:1000:1024:0/120 ip6:2607:b400:000a:0280::/64 mx a ~all"

Historically, they were stored in the SPF record type, however this is considered deprecated, and should not be used.

A record consists of 2 parts: a version declaration, and a set of declarations (called "mechanisms" in the RFC) defining allowed senders. Every declaration is prefixed with one of + - ~ ?, which are defined to mean "pass", "fail", "softfail", and "none", respectively (with an implied pass, if no modifier is prefixed).

"all" and "include" are special declarations; "include" references the SPF for another domain, and "all" matches any other hosts (and must be the last declaration).

We provide examples and explanation of the most common mechanisms for convenience. Full EBNF syntax is provided in the RFC.


This declaration performs a DNS lookup to see if address records match the sender. e.g.:

v=spf1 ~all

In this example, if the A or AAAA record for resolves to the sending address, this will pass, and all others will soft fail. If "a" is provided without a domain, it is assumed to be the same as that for the record. Thus, v=spf1 a ~all in a record for would be equivalent to v=spf1 ~all Further, this declaration type can be used with CIDR prefixes. Consider:

$ dig +short a && dig +short aaaa

The above spf for could be reduced to:

v=spf1 ip6:2607:b400:000a:0280::/64 mx ~all

Notice this uses "dual cidr" notation specified in the RFC. This is tricky behavior, and we do not recommend it.


This declaration behaves similarly to the "a" declaration above, however it triggers lookups for the specified MX record, instead.

"ip4" and "ip6"

These declarations test whether the source IP is in a given IP network. In the WUVT example from above, it allows ip4:, ip6:2001:468:c80:a102:1:1000:1024:0/120, and ip6:2607:b400:000a:0280::/64 with CIDR prefixes behaving as expected.


The include mechanism is designed to allow the SPF records from other domains to be evaluated as an ancilliary statement, for example when someone else provides your mail services.

Please note the following from the RFC:

In hindsight, the name "include" was poorly chosen. Only the evaluated result of the referenced SPF record is used, rather than literally including the mechanisms of the referenced record in the first. For example, evaluating a "-all" directive in the referenced record does not terminate the overall processing and does not necessarily result in an overall "fail". (Better names for this mechanism would have been "if-match", "on-match", etc.)


As previously noted, the "all" declaration matches any other source addresses, and must be last. The astute reader will note that we always specify ~all instead of -all. This is because it is fairly difficult to accurately predict what source IPs will be used for sending mail in certain configurations, and implementations vary on how strictly they enforce a hard fail. Thus, a softfail is less likely to cause issues.

Next: Appendix 3 Up: Network Liaison's Handbook Previous: Appendix: IP Address Registry (IPR) Eric C. Landgraf 2019-05-07