Knowledge base

check-spf-inbound-max-void-lookups

<source>

How many empty DNS answers to tolerate during an SPF check
in the configuration this is a linecheck-spf-inbound-max-void-lookups 2inside the block <source>
The threshold beyond which the check gives up

An SPF record can refer to other domains, and those to further ones. While parsing it, the node makes DNS queries, and some of them come back empty: the name does not exist or the answer is empty. This line sets how many such answers to tolerate before declaring the check a permanent error.

The default is two. The manual explains why: a low threshold prevents your check from being turned into a way to load someone else’s resolver. The flip side is named in the same place — complex records may fail to parse.

When the threshold is exceeded, there is one outcome: a permanent error. What to do with it is decided by reject-spf-check-permerror.

The main trap is that the threshold is responsible for refusals elsewhere. By tightening it, you increase the share of permanent errors without touching a single refusal line. And if refusal on this outcome is enabled, messages will start being rejected — for a reason that may not even be in the sender’s record.
What the Hub does. It does not write it: the inbound check is not enabled. The vendor default is reasonable anyway — it both protects the resolver and does not break most records.
Recommended values

Keep the default. It balances the two extremes named in the manual.

Raise it only after figuring out whose records fail. A complex record at a large sender is a reason; a dozen unknown domains are not.

There is no point lowering it. Below two, only the simplest records remain, and the load on the resolver drops insignificantly.

The advice about raising and lowering is ours. From the manual here come the default, the meaning of the threshold and both sides of the choice: protection from load versus complex records.
Example configuration
# the default is kept on purpose
<source 0/0>
    check-spf-inbound true
    check-spf-inbound-max-void-lookups 2</source>
The line is useful as a reminder: it is exactly this threshold that produces some of the permanent errors for which messages are then rejected.
yellow — — directive names, blue — blocks. They are highlighted the same way in the text of the card.
▲

Common mistakes and problems

what people get wrong and how it ends1 mistake▶

Tightening the threshold with refusal enabled

our experience

What it costsThe share of permanent errors grows, and messages start being rejected — for a reason that may not be in the sender’s record at all. Not a single refusal line changed.
How to spot itRefusals grew after an edit unrelated to refusals.
How to do it rightRestore the default or turn off refusal on permanent error.
Compatibility
PowerMTA 6.0
described in the manual of this version · platform: any
The vendor does not state the first version for every directive — there is no note here. The name also appears in the 5.0 edition of the manual.
Reference data
Syntax
check-spf-inbound-max-void-lookups <integer>
Values
an integer number of empty DNS answers; the default is 2
Default
2 § 3.3.7.24
Applying
reload, no restart needed
Queue
will not touch what is already queued
Where to write it
<source>
How many times
once in the block
Everything here comes from the manual, with the section.
Inheritance
The value is set in a <source> block and applies to mail submitted by a matching source; with several matching blocks the most precise one wins. It works only with check-spf-inbound enabled, and exceeding it is handled by reject-spf-check-permerror. It is picked up by pmta reload.
How to check on the node
The value is set in the source configuration; how many checks hit the threshold is visible from the share of permanent errors:
grep -n max-void-lookups /etc/pmta/config
grep -ci 'permerror' /var/log/pmta/pmta.log
The log cannot tell exceeding the threshold apart from a syntax error in a record — both give the same outcome. The Hub does not write this line.
In the Hub
Cost of a mistake
reversible: harms only while it is set
Scenarios
Receiving inbound mail