Fixing SPF PermError and DKIM 2048-bit DNS Chunking Syntax Published: 27 Aug, 2026
The Anatomy of Modern DNS Email Authentication Failures
Stricter validation routines enforced by major receiving Mail Transfer Agents (MTAs) like Google, Microsoft 365, and Yahoo have reduced mailbox tolerance for syntactically ambiguous DNS records. Two root causes account for the majority of unhandled authentication drops: the RFC 7208 ten-lookup limit leading to an SPF PermError, and improperly escaped or non-concatenated 2048-bit DKIM keys that break the RFC 1035 255-character single-string limit.
SPF PermError: Void Lookups and the 10-Query Mechanism
RFC 7208 Section 4.6.4 dictates that the evaluation of an SPF record must not exceed 10 direct DNS queries. MTAs evaluate these mechanisms recursively. If the counter surpasses 10, the receiving MTA immediately terminates evaluation and assigns a permanent error: SPF PermError.
The mechanisms that count toward the 10-query limit are:
- include: triggers a TXT lookup for the target domain.
- a: triggers an A or AAAA lookup for the specified host.
- mx: triggers an MX lookup, followed by A/AAAA lookups for each host returned.
- ptr: triggers reverse-DNS lookups (deprecated and forbidden in strict environments).
- exists: triggers an A lookup against a dynamic macro.
- redirect= modifies the query pointer to another zone.
Mechanisms such as ip4, ip6, and all are static evaluations and cost zero DNS lookups.
RFC 7208 Void Lookup Penalty
In addition to the 10-lookup limit, receiving resolvers enforce a maximum of two void lookups. A void lookup occurs when an a, mx, or include mechanism resolves to NXDOMAIN or returns an empty answer section (NOERROR with zero answers). Exceeding two void lookups results in an instantaneous PermError, even if the total DNS query counter remains well under 10.
Flattening Complex SPF Records
To eliminate recursive include chains, replace third-party include statements with resolved CIDR blocks directly within your zone, or leverage an automated macro-based or API-driven SPF flattening pipeline. Below is an example of an over-budget record versus a flattened, RFC-compliant equivalent:
; INVALID: Exceeds 10-lookup threshold across nested includes
v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org include:servers.mcsv.net -all
; VALID: Flattened direct CIDR representation
v=spf1 ip4:198.51.100.0/24 ip4:203.0.113.4/30 ip6:2001:db8::/32 include:_spf.google.com -allDKIM 2048-Bit Key DNS Chunking (RFC 1035 / RFC 6376)
Industry best practice mandates 2048-bit RSA keys for DKIM signatures. A base64-encoded 2048-bit public key string contains approximately 390 characters. However, RFC 1035 Section 3.3.14 establishes that a single DNS <character-string> within a TXT record cannot exceed 255 octets.
When storing records exceeding 255 bytes, DNS specifications require breaking the payload into multiple quoted strings within a single TXT record: ("string1" "string2"). The receiving resolver concatenates these strings seamlessly with no added whitespace.
BIND Zone File Representation
In raw BIND zone configurations, wrap the multi-string payload in parentheses. If you fail to break the string or concatenate it incorrectly, BIND either rejects the zone load with a ran out of space error or the MTA receives a truncated key payload, throwing dkim=permerror (invalid key).
; Correct BIND syntax for 2048-bit DKIM Key
mail._domainkey.example.com. IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Y1+kU9+Q9ZqV5sK1k5mK9..." "...Ab3CdEfGhIjKlMnOpQrStUvWxYz1234567890/AbCdEfGhIjKlMnOpQrStUvWxYz==" )API and Cloud Provider Quirks
Different DNS service providers handle this chunking differently at the API and dashboard layer:
- Cloudflare: Automatically splits TXT records exceeding 255 characters via its web dashboard and API. Entering the raw monolithic string is automatically converted to RFC 1035 chunks on edge nameservers.
- AWS Route 53: Requires explicit manual chunking in the control panel and raw API JSON. Enclose each segment in independent quotation marks inside a single text field:
"v=DKIM1; ... chunk1" "chunk2...". - NS1 / PowerDNS: Requires the raw payload with backslash-escaped inner quotes depending on whether the record is submitted via REST JSON or native zone files.
DMARC Identifier Alignment Constraints
DMARC evaluates the authentication state of SPF and DKIM through Identifier Alignment. Passing SPF and DKIM independently is insufficient if neither aligns with the RFC 5322 From header domain visible to the end recipient.
| DMARC Tag | Relaxed Mode (Default: r) | Strict Mode (s) |
|---|---|---|
| aspf (SPF Alignment) | Organizational domain match (e.g., mail.example.com aligns with example.com). | Exact FQDN match required (mail.example.com fails against example.com). |
| adkim (DKIM Alignment) | The d= tag in the DKIM signature matches the organizational domain of the From: header. | The d= tag must match the exact FQDN of the From: header. |
Optimized DMARC Deployment Record
To enforce a strict policy without dropping legitimate unaligned mail prematurely during deployment, configure the DMARC TXT record at _dmarc.example.com with granular reporting flags:
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; aspf=r; adkim=s; pct=100; rua=mailto:[email protected]; ruf=mailto:[email protected]; fo=1"Validating Records from the Command Line
Do not rely on web-based cache checkers to debug authentication syntax. Use dig and low-level key validation utilities to parse raw payloads directly from authoritative nameservers.
Querying Raw TXT Representation with dig
# Check SPF and inspect wire-format string boundaries
dig @ns1.example.com example.com TXT +noall +answer
# Query DKIM selector key and check chunking syntax
dig @ns1.example.com mail._domainkey.example.com TXT +noall +answerWhen executing the DKIM check, observe the output. If the response contains two quoted strings adjacent to each other like "v=DKIM1; ..." "...==", the resolver correctly received the multi-part TXT payload.
Validating Local Keys with opendkim-testkey
If you run a local OpenDKIM or Rspamd daemon, verify the key pairing directly against DNS before updating production routing rules:
opendkim-testkey -d example.com -s mail -vvvA successful validation returns opendkim-testkey: key OK. If the output reports key not secure, verify that DNSSEC is either intentionally inactive or correctly signed across the delegation path.