DEV Community

ReadyStack
ReadyStack

Posted on Originally published at getreadystack.com

SPF & DMARC Record Lint

Six email-auth faults in one zone file

Six findings in one four-record zone file — for the platform engineer who keeps DNS in git and adds a sending vendor every quarter. The result first, the explanation afterwards:

_fixtures/dirty.zone — 15 checks — 6 findings (4 errors, 2 warnings)
ERROR  L5  spf_lookup_limit  SPF record needs 11 DNS lookups — the limit is 10 (RFC 7208 §4.6.4). Receivers return PermError and treat the domain as having no SPF at all.
ERROR  L5  spf_ptr           The 'ptr' mechanism is deprecated and must not be published (RFC 7208 §5.5).
ERROR  L6  spf_pass_all      '+all' passes SPF for every sender on the internet — the same as publishing no SPF.
WARN   L7  dmarc_p_none      'p=none' only asks for reports; nothing that fails alignment is blocked.
WARN   L7  dmarc_no_rua      No 'rua=' tag, so no aggregate reports are ever sent.
ERROR  L8  dkim_p_empty      Empty 'p=' means the key is revoked — every signature from this selector fails.
Enter fullscreen mode Exit fullscreen mode

The zone is plain: one SPF record for the mail host, one for a legacy sender, one DMARC record, one DKIM selector. Every line is valid. BIND loads it, dig prints it back exactly as written, and a language model asked to review it will say it looks fine.

Line 5 is the expensive one. RFC 7208 §4.6.4 caps SPF evaluation at ten DNS lookups — include, a, mx, ptr, exists and redirect= each cost at least one, and every include: resolves to a record that can spend more. At lookup eleven the receiver stops and returns PermError, and most receivers treat PermError like a domain with no SPF record. If DMARC was passing on SPF alignment alone, DMARC fails with it. Nothing in the editor or the deploy log turns red, and the record stays live until somebody notices invoices landing in spam.

Eight includes, an a:, an mx and a ptr: is eleven. Nobody adds eleven at once: you add the fourth vendor, then the sixth, then the eighth, and the record crosses the line on an ordinary Tuesday. The ptr: should not be there either — §5.5 deprecated it, receivers may ignore it, and it costs a lookup per candidate host.

Line 6 authorises every host on the internet to send as the domain: +all is the protection of publishing nothing with the paperwork of publishing something. Line 7 is monitoring with the monitor unplugged — p=none blocks nothing, and with no rua= no aggregate report is ever sent, so there is no data to move off it with. Line 8 is a DKIM selector whose public key has been revoked to an empty p=: every signature made with it fails.

Fix those six and the same zone returns zero findings.

Why the free checkers do not catch it

Because they resolve. Every online SPF tool takes a domain name, queries DNS, and answers about the record already live — which is exactly the record already costing you mail. The one you want checked is in a pull request, on a domain that does not resolve yet, behind a Terraform variable. A chatbot will count your include: chain by guessing.

SPF & DMARC Record Lint reads v=spf1, v=DMARC1 and v=DKIM1 strings wherever you actually write them — BIND zone files, aws_route53_record and cloudflare_record blocks, Kubernetes and Ansible YAML, .env files, docs — and runs 15 checks on each one: seven for SPF, five for DMARC, three for DKIM. Findings land on the line in the Problems panel with the RFC section and the edit that fixes them. No DNS queries, no network, no account, no telemetry.

The file in front of you is checked in full, by all 15 rules, free and forever, with nothing timed or watermarked. The licence key is a different axis: sweeping every zone, Terraform and YAML file in the workspace in one pass, and keeping one dated Markdown audit report you can attach to the change ticket. $29 once, one key per person or CI seat, 7-day full refund — against the $50–$150 an hour a freelance deliverability specialist bills, most of the first hour going on reading the records you already have.


Free in your browser (the same rules): https://getreadystack.com/tools/spf-dmarc-record-lint

Licence ($29, once, 7-day refund): https://buy.polar.sh/polar_cl_lTz1ceauyoB7vrNgatSTcYZd8Oyia1VfFLlxg3fcI1A

Top comments (0)