Applies to comms.email-authentication.

Covers comms.email-authentication. Related: comms.sender-identity, comms.delivery-alerting, env.env-config-not-in-db.

Automated mail from a website is a claim - this message comes from the client's domain - and SPF, DKIM and DMARC are the three DNS records that let a receiving server check it. Without them the mail is accepted, filed in spam or dropped, and nothing comes back to say which.

The failure, and why nothing reports it

Local development catches mail. Mailpit, MailHog, a log file: they accept everything and verify nothing, by design. The form works, the notification arrives, the body looks right, and the mail half of the build is signed off having never met a receiver that cares.

Production is the first time anything evaluates the claim. Nothing about the site changed: the From header still says the client's domain, the sending host is now the shared host or a relay, and the client's zone does not authorise it. No error surfaces. The form returns 200 and the submission is stored, which is comms.submission-delivery working as designed; a rejection bounces to a noreply nobody opens; mail filed in spam produces no signal at all.

The loss is therefore silent, cumulative and unmeasurable afterwards: nobody can say how many enquiries went missing, because there is no number to count. comms.delivery-alerting catches a connection that breaks. It does not catch mail that was accepted and quietly binned.

What each of the three does

Record Lives at Answers Common reason it fails
SPF TXT on the domain Is this IP allowed to send for this domain? The host or relay was never added to it
DKIM TXT at <selector>._domainkey.<domain> Was this signed by a key the domain publishes, and is it unaltered? Nothing signs. WordPress alone signs nothing
DMARC TXT at _dmarc.<domain> Do the domains that passed match the visible From, and what happens if not? Published before the other two work

Alignment is the part that surprises people

SPF and DKIM are checks; DMARC is a policy about them, which is why the rule says publish it last, at p=none, once both pass. SPF is evaluated against the envelope sender, shown in the headers as smtp.mailfrom; DKIM against the signing domain, the d= in the signature. Neither looks at the From line the reader sees. DMARC alone requires a passing domain to line up with that From.

So spf=pass dkim=pass dmarc=fail is real, not a contradiction: a relay authorises and signs with its own domain, both checks pass honestly, and neither passing domain is the client's. Same mechanism as comms.sender-identity - the visitor's address in From makes every notification claim a domain you hold no records in. Alignment is our own summary; the cited page does not describe it.

Reading the result on a received message

Gmail, open the message, three dots, Show original.

Authentication-Results: mx.google.com;
       dkim=pass header.i=@example.com header.s=s1 header.b=Qk3vB1nZ;
       spf=pass (google.com: domain of bounce@mail.relay.net designates
            203.0.113.9 as permitted sender) smtp.mailfrom=bounce@mail.relay.net;
       dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=example.com
From: Example <hello@example.com>

Read header.from= first: the domain being claimed. Then smtp.mailfrom= and the DKIM header.i=, the domains that actually passed. If they do not match, dmarc fails whatever passes sit above it. File the whole original, not a checking service's ticks - only the header names which domain passed.

What the source asks for, and what this standard added

Google's sender guidelines, under Requirements for all senders:

Set up SPF or DKIM email authentication for your sending domains.

Or, not and. Every sender needs one of the two; all three including DMARC is asked only of bulk senders, which Google puts at 5,000 a day to Gmail and allows at p=none. Yahoo matches. A contact form is not bulk, so the measurable - all three passing on one dated send - is ours. DMARC costs one DNS row, rejects nothing at p=none, and turns a silent question into a reported one.

Neither reference build does it

Both delivered themes score aspirational, differently. One sends From a .com address while the site is on the matching .fr, and that .com has no DNS at all - no nameservers, no A record, no MX - so a receiver has nothing to look up, and production sets no SMTP override, so that default ships. The other's production From is on gmail.com through a third-party relay: a domain the project does not control, so nobody on it can publish SPF or DKIM. Its own notes call that "temporary" and "still pending"; it went live like that. Its production database also has the SMTP switch off while its production environment file names the relay - the split env.env-config-not-in-db exists to stop. Both zones already publish all three records, so both could send aligned today. Neither does; neither ever made a test send. The starter theme ships nothing: one comment in inc/mail.php.

Ask who holds the zone at kickoff

Client-owned by default, agency-owned if Colibrity holds the DNS. Ask early: env.staging-sends-nothing-live forbids staging mailing real addresses, so the send that proves this rule is a production send against live DNS. Requested in launch week, it is a record you wait on during launch.

One trap once you have access. A domain sends from more places than its website - newsletter tool, CRM, invoicing, somebody's desktop client - and SPF is one record per domain, evaluated whole. Tighten it to -all without enumerating them and you break their mail, not yours. And reading the records back with dig +short TXT only proves they exist, never that the site sends through them, which is why this rule's evidence is a received header. Both reference builds pass the lookup today.