Plainwork

Plainwork / Mail Auth Fix Sheet / Sample Mail Auth Fix Sheet: SPF, DKIM, DMARC

Sample: a Mail Auth Fix Sheet

These are worked samples of what the $39 service returns. Sample 1 is Plainwork's own domain, read from live public DNS (one record missing). Sample 2 uses a domain that does not exist: shop.example, a reserved name, with a DNS zone we constructed to be messy (two SPF records, no DMARC). It shows DELETE and REPLACE with the old value next to the new, and the SPF budget before and after, each counted by the open-source checker reading that zone file. We never publish a sample about a real company that did not order one. Every DNS line in sample 1 can be re-checked with the free checker, every quotation is checked against the source page by a script, and the invented parts are marked ASSUMED. Back to the service.

Sample, produced by running the service's own playbook on a real public domain: Plainwork's own. Brief: domain plainworkdigital.com; senders: Resend (order and receipt emails) and Cloudflare Email Routing (forwards hello@ to a private inbox); problem: "no DMARC, want to be sure customer email passes before we enforce anything"; volume: under 100 messages a day. DNS read 2026-10-15 01:40 UTC over Cloudflare DNS-over-HTTPS. AI-written from public sources; not legal or compliance advice; no deliverability promise.

1. The answer first

  1. Publish one new record: DMARC at p=none with a report address (section 4). It is the only thing missing. SPF and DKIM for both senders are already in place and nothing needs deleting.
  2. Leave SPF alone. The root record uses 1 of 10 lookups, and Resend does not need an include at the root because it sends from the send subdomain.
  3. Do not enforce yet. Forwarded mail through Cloudflare Email Routing is the part we cannot judge from DNS, so wait for two weeks of reports, then step up (section 6).

2. What we found

CheckResultEvidence (raw DNS)
One SPF record at the rootPASSplainworkdigital.com TXT "v=spf1 include:_spf.mx.cloudflare.net ~all"
SPF lookups (limit 10) and voids (limit 2)PASS1 lookup, 0 voids (engine count)
SPF for the Resend return-pathPASSsend.plainworkdigital.com is a CNAME to send.forge.rmta.net., whose TXT is "v=spf1 ip4:52.3.252.119 ip4:44.222.39.36 ip4:199.249.231.0/24 ~all"
DKIM for ResendPASSresend._domainkey.plainworkdigital.com publishes a p= public key
DKIM for Cloudflare Email RoutingPASScf2024-1._domainkey.plainworkdigital.com publishes a v=DKIM1 key
DMARC recordFAIL_dmarc.plainworkdigital.com TXT returns no answer (status 3, name does not exist)
MXPASSroute1.mx.cloudflare.net., route2.mx.cloudflare.net., route3.mx.cloudflare.net.
MTA-STSNOT SETno _mta-sts record; optional, and not needed for the questions in the brief
Blocklists, inbox placement, reputationUNKNOWNnot visible from DNS; we do not guess

3. Your senders

SenderWhat the vendor requires (quoted)In your DNS nowAction
Resend"By default, Resend uses the send subdomain for the Return-Path address." and "An email must pass either SPF or DKIM checks (but not necessarily both) to achieve DMARC compliance and be considered authenticated."send CNAME and resend._domainkey key presentNone
Cloudflare Email Routing"v=spf1 include:_spf.mx.cloudflare.net ~all" at the root, and "Email Routing uses its own DKIM selector"Both present, selector cf2024-1None

4. Records to publish

Add (one record):

Type:  TXT
Name:  _dmarc
Value: v=DMARC1; p=none; rua=mailto:[email protected];
TTL:   Auto
Old:   none (the name does not exist today)

The rua address is ASSUMED: we do not know a mailbox you control, so we used [email protected]. It must exist before you publish the record. With Cloudflare Email Routing, create a routing rule that forwards that address to your inbox. The format follows Resend's own example, v=DMARC1; p=none; rua=mailto:[email protected];.

5. SPF lookup budget

BeforeAfter
Root recordv=spf1 include:_spf.mx.cloudflare.net ~allunchanged
Lookups used (of 10)11
Void lookups (of 2)00

Room left: 9 lookups. A future sender that supports a sending subdomain (as Resend does) costs the root record nothing.

6. Rollout plan (dates from the delivery date, 2026-10-15)

DateStepGo/no-go check before the next step
2026-10-15Create the report mailbox rule, then publish p=noneRecord visible in the checker; a test message from each sender shows dmarc=pass in its headers
2026-10-29Read two weeks of aggregate reportsEvery legitimate source passes aligned SPF or DKIM on 7 consecutive days, including mail forwarded by Cloudflare
2026-10-29Change to p=quarantine; pct=10No legitimate source failing in the next week's reports
2026-11-05pct=50Same check
2026-11-12pct=100Same check
2026-11-26p=reject only if all of the above heldRollback: set p=none again; DNS changes take effect after the TTL

Resend's own advice matches: "we suggest starting with a policy of p=none; before moving to a stricter policy".

7. How to verify

  • After each change, run dig +short TXT _dmarc.plainworkdigital.com (or nslookup -type=TXT _dmarc.plainworkdigital.com), or use the free checker at https://plainworkdigital.com/d20261015a8/email-auth-checker.
  • In Gmail open a received message, choose Show original, and read the lines SPF, DKIM and DMARC: you want PASS on DMARC.
  • Check the first aggregate report for any source you did not list.

8. What we could not establish

  • Whether mail forwarded by Cloudflare Email Routing passes DMARC at its final destination: DNS cannot show it; the reports will.
  • Any sender not named in the brief that uses the domain (marketing tools, a helpdesk): the reports will list them.
  • DKIM selectors outside the 24 common names we probe: not-found is not "off".
  • Microsoft's published requirement applies above 5,000 messages a day; your stated volume is under 100.

9. Sources (fetched 2026-10-15)

SourceQuoted
https://support.google.com/a/answer/81126?hl=en"Set up SPF or DKIM email authentication for your sending domains." (all senders) and "Set up DMARC email authentication for your sending domain." (5,000 or more a day)
https://senders.yahooinc.com/best-practices/"Publish a valid DMARC policy with at least p=none"
https://substrate.office.com/ip-domain-management-snds/postmaster/Policies"Domains sending more than 5,000 emails per day to Outlook.com accounts must fully be compliant with SPF, DKIM, and DMARC."
https://resend.com/docs/dashboard/domains/dmarcthe two Resend quotes in sections 4 and 6 and the DMARC-compliance quote in section 3
https://resend.com/docs/dashboard/domains/custom-return-path.md"By default, Resend uses the send subdomain for the Return-Path address."
https://developers.cloudflare.com/email-routing/setup/email-routing-dns-records/"v=spf1 include:_spf.mx.cloudflare.net ~all"; "Email Routing uses its own DKIM selector"
https://www.rfc-editor.org/rfc/rfc7208.txt"SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation, to avoid unreasonable load on the DNS. If this limit is exceeded, the implementation MUST return "permerror"."

Sample 2: two SPF records, three senders, no DMARC (worked example on a constructed domain)

Same playbook, a messier domain. This domain is constructed for illustration. shop.example is a reserved name (RFC 2606) that cannot belong to anyone, and its DNS is a zone file we wrote and keep in the open-source repo (run the commands below from a clone of it) (examples/shop.example.json); it is not a real site and not a customer. The checker engine reads that file instead of live DNS, so every count and every record quoted below is produced by the same code as a real order. The senders (Fastmail for staff mailboxes, SendGrid for password resets and receipts, Mailchimp Transactional/Mandrill for member digests), the problem ("password reset emails land in Gmail spam") and the volume (about 3,000 a day, mostly Gmail) are ASSUMED. Vendor quotations in sections 3 and 8 are real and fetched 2026-10-15; the vendors' own SPF and DKIM names are shown as they appear in their setup pages.

1. The answer first

  1. Fix SPF first: the root publishes two v=spf1 records, which receivers must treat as a permerror. Merge them into one record and delete the other (section 4). Until then SPF fails for every message, including the password resets.
  2. Publish DMARC at p=none (section 4). There is no _dmarc record today, and Google's bulk-sender rules ask for one from 5,000 messages a day; at 3,000 a day you are below that line, but Yahoo asks for at least p=none.
  3. Do not enforce yet. Keep DMARC at p=none for two weeks and read the reports before any quarantine step (section 6).

2. What we found

CheckResultEvidence (raw DNS)
One SPF record at the rootFAILshop.example TXT "v=spf1 include:sendgrid.net ~all" and, at the same name, "v=spf1 include:spf.messagingengine.com ?all"
SPF lookups (limit 10) and voids (limit 2)FAILrecord 1 alone: 2 lookups, 0 voids (include:sendgrid.net plus the include:ab.sendgrid.net inside it); record 2 alone: 1 lookup. Two records is a permerror whatever the count
Terminating all termWARNrecord 2 ends in ?all, which gives receivers no instruction; record 1 ends in ~all
DKIM for FastmailPASSfm1._domainkey.shop.example is a CNAME to fm1.shop.example.dkim.fmhosted.com.; fm2 and fm3 are CNAMEs too
DKIM for SendGridPASSs1._domainkey.shop.example and s2._domainkey.shop.example are CNAMEs to s1.domainkey.u0000001.wl001.sendgrid.net. and its s2 twin; both resolve to a p= key
DKIM for Mailchimp Transactional (Mandrill)PASSmandrill._domainkey.shop.example is a TXT record with a v=DKIM1; k=rsa; p= key (the older selector; the vendor's current page documents mte1 and mte2, section 3)
DMARC recordFAIL_dmarc.shop.example TXT returns no answer
MXPASSin1-smtp.messagingengine.com., in2-smtp.messagingengine.com.
MTA-STSNOT SETno _mta-sts record; optional
SendGrid return-path (the em host SendGrid's page describes)UNKNOWNits name carries an account number we cannot enumerate; check the SendGrid dashboard, Sender Authentication
Blocklists, inbox placement, reputationUNKNOWNnot visible from DNS; we do not guess

3. Your senders

SenderWhat the vendor requires (quoted)In your DNS nowAction
Fastmailv=spf1 include:spf.messagingengine.com ?all and the DKIM CNAMEs fm1._domainkey, fm2._domainkey, fm3._domainkeyinclude is in record 2; fm1 to fm3 presentKeep, merge into the single SPF record
SendGridv=spf1 include:sendgrid.net ~all (on the vendor's em host) and the s1, s2 DKIM CNAMEsinclude in record 1; s1, s2 presentKeep the root include until the dashboard confirms the em return-path is set up
Mailchimp Transactional (Mandrill)"Add two new CNAME records with the names mte1._domainkey.example.com and mte2._domainkey.example.com"; values dkim1.mandrillapp.com and dkim2.mandrillapp.comlegacy mandrill TXT key only; no mte1 or mte2Optional ADD, once the Mandrill dashboard (View DKIM settings) shows these; SPF include for Mandrill: UNKNOWN, the fetched page names none

4. Records to publish

DELETE the second SPF record:

Type:  TXT
Name:  @
Value: v=spf1 include:spf.messagingengine.com ?all
Action: DELETE (its include moves into the record below)

REPLACE the first SPF record:

Type:  TXT
Name:  @
Old:   v=spf1 include:sendgrid.net ~all
New:   v=spf1 include:spf.messagingengine.com include:sendgrid.net ~all
TTL:   Auto

The new record uses 3 of 10 lookups, counted by script (section 5). It ends in ~all, not the ?all Fastmail's page shows: that is our choice, so that unlisted senders are marked as soft failures rather than ignored. ADD DMARC:

Type:  TXT
Name:  _dmarc
Value: v=DMARC1; p=none; rua=mailto:[email protected];
Old:   none (the name does not exist today)

The rua address is ASSUMED; the mailbox must exist before you publish.

5. SPF lookup budget

BeforeAfter (A: keep SendGrid at the root)After (B: only if SendGrid's em return-path is confirmed)
SPF records at the root2 (permerror)11
Lookups used (of 10)2 in record 1, 1 in record 231
Void lookups (of 2)000
Counted bynode cli.mjs shop.example --zone examples/shop.example.jsonnode cli.mjs shop.example --zone examples/shop.example.json --spf "v=spf1 include:spf.messagingengine.com include:sendgrid.net ~all"node cli.mjs shop.example --zone examples/shop.example.json --spf "v=spf1 include:spf.messagingengine.com ~all"

Room left after A: 7 lookups. Option B frees 2 more, but only after you have checked the dashboard: do not choose it from DNS alone.

6. Rollout plan (dates from the delivery date, 2026-10-16)

DateStepGo/no-go check before the next step
2026-10-16Merge SPF as in section 4, then publish p=noneThe checker shows one SPF record and 3 of 10 lookups; a test reset email from SendGrid and a message from Fastmail both show spf=pass or dkim=pass
2026-10-30Read two weeks of aggregate reportsEvery legitimate source passes aligned SPF or DKIM on 7 consecutive days, Mandrill digests included
2026-10-30Change to p=quarantine; pct=10No legitimate source failing in the next week's reports
2026-11-06pct=50, then pct=100 a week laterSame check; rollback is p=none

7. What we could not establish

  • Whether SendGrid's em return-path is configured, and whether Mandrill needs any SPF text at all: UNKNOWN from DNS and from the pages we fetched.
  • Which other services send as the domain: the DMARC reports will list them.
  • Gmail placement of the password resets: not visible from DNS; fixing SPF removes one cause only.

8. Sources (fetched 2026-10-15)

SourceQuoted
https://www.fastmail.help/hc/en-us/articles/360060591153-Manual-DNS-configurationv=spf1 include:spf.messagingengine.com ?all; the fm1, fm2 and fm3 DKIM CNAMEs
https://www.twilio.com/docs/sendgrid/ui/account-and-settings/how-to-set-up-domain-authenticationv=spf1 include:sendgrid.net ~all
https://mailchimp.com/developer/transactional/docs/authentication-delivery/"Add two new CNAME records with the names mte1._domainkey.example.com and mte2._domainkey.example.com"
https://www.rfc-editor.org/rfc/rfc7208.txt"SPF implementations MUST limit the total number of those terms to 10 during SPF evaluation, to avoid unreasonable load on the DNS. If this limit is exceeded, the implementation MUST return "permerror"."

More from the catalogue

Other small tools

See everything →