Your contact form fires, the log says “sent”, and the message lands in the recipient’s spam folder. Three DNS records, SPF, DKIM and DMARC, are almost always the missing piece. This guide explains what each one does and how to get them right.
include value and the DKIM key or CNAMEs they ask you to add. Your provider’s documentation will have both.
Download it as a prompt file, paste it into Claude, ChatGPT, Gemini or any LLM, and it will walk you through diagnosing and fixing your email deliverability one step at a time.
↓ Download as LLM promptBefore adding any records, establish which problem you actually have. There are two distinct failure modes.
Transport failure: the message never left your server, or was rejected immediately. Check your mail plugin or application logs. An error like “connection refused” or “authentication failed” means your sending credentials or SMTP settings are wrong. No DNS record will fix that.
Authentication failure: the message was sent successfully but landed in spam. This is the SPF, DKIM and DMARC problem this guide addresses. The fast way to confirm it is to send a test message to yourself at Gmail or another major provider, open it, and look at the headers.
In Gmail, open the message, click the three-dot menu in the top right, and choose “Show original”. Near the top you will see lines like this:
SPF: PASS with IP 192.0.2.10 DKIM: FAIL DMARC: FAIL
Any FAIL tells you exactly which record needs attention. If all three show PASS and mail is still going to spam, the issue is content or sender reputation, not authentication.
SPF (Sender Policy Framework) is a TXT record on your domain that lists the servers permitted to send email on its behalf. When a message arrives, the receiving server looks up this record and checks whether the sending IP is on the list.
A typical SPF record for a site that sends via Mailgun looks like this:
# DNS TXT record on yourdomain.com
v=spf1 include:mailgun.org -all
Breaking that down: v=spf1 declares it as an SPF record; include:mailgun.org says “also allow whatever servers Mailgun publishes”; -all says “reject anything else” (a hard fail).
If you use ~all instead, that is a soft fail: recipients are advised to treat other senders with suspicion rather than reject them outright. Hard fail (-all) is the more decisive choice for a domain you control.
v=spf1 include:mailgun.org include:sendgrid.net -all
include: in your SPF record can itself pull in more includes, and SPF only permits 10 DNS lookups in total across the chain. Large providers can use several. If you hit the limit, SPF returns a permerror and mail fails. Keep your includes to the minimum you actually need.DKIM (DomainKeys Identified Mail) works differently from SPF. Rather than listing who can send, it proves that a specific message was signed by the holder of your domain, and that the message was not altered in transit.
Your mail provider generates a pair of cryptographic keys. They give you the public key to publish as a DNS record; they keep the private key and use it to sign every outgoing message. The receiving server fetches the public key from DNS and uses it to verify the signature in the message headers.
The DNS record is a TXT entry at a subdomain shaped like this:
# TXT record at: selector._domainkey.yourdomain.com
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUA...
The selector part is chosen by your provider (for example mx, k1, or a custom name you set). The p= value is the public key itself, which your provider gives you to paste in.
Some providers use CNAMEs instead of a TXT record: they ask you to add a CNAME that points to a subdomain they control, so they can rotate keys without you touching DNS again. Either way, your provider’s setup guide will tell you exactly what to add and where.
DMARC (Domain-based Message Authentication, Reporting and Conformance) is a TXT record that tells receiving servers what to do when a message fails SPF or DKIM, and where to send reports about what is happening.
A DMARC record goes at a specific subdomain of your domain:
# TXT record at: _dmarc.yourdomain.com v=DMARC1; p=none; rua=mailto:[email protected]
The key parts:
p=none means “monitor only, do not take action yet”. This is the right starting point.p=quarantine means “put failing messages in spam”.p=reject means “discard failing messages entirely”.rua= is the address where aggregate reports are sent. These reports show you which servers are sending as your domain and whether they are passing authentication.Start at p=none, watch the reports for a week or two, confirm that your own legitimate mail is all passing, then move to p=quarantine and eventually p=reject.
rua address) show you exactly what is claiming to be from your domain. They often reveal third-party services sending on your behalf that you had forgotten about, which is useful before you tighten the policy.DNS changes can take a few minutes to an hour to propagate. Once they are live, verify properly rather than trusting that the records are there.
Use mail-tester.com. Send a test message to the address it gives you, then check your score. It reads the actual received headers and shows pass or fail for each authentication check, plus a plain-English explanation of anything that needs attention.
Use Gmail’s “Show original”. Send a test to a Gmail address and open the raw headers as described in step 1. You want to see this:
SPF: PASS DKIM: PASS DMARC: PASS
All three passing is the only reliable confirmation. Your DNS panel showing the records is necessary but not sufficient: a record can be there and still fail if the value is wrong, the selector is mismatched, or the SPF record has a syntax error.
DMARC introduces a concept called alignment, which is why having one authentication method is often still not enough.
For DMARC to pass, one of two conditions must be true. Either SPF passes and the domain in the SPF check (the envelope sender) aligns with the domain in the visible From header. Or DKIM passes and the d= domain in the DKIM signature aligns with the domain in the From header.
This matters because a provider’s shared SPF record proves that the message came from their servers, but it does not prove it came from your domain. If you send from [email protected] via a provider and only SPF is set up, the SPF check passes for the provider’s domain, not yours. DMARC alignment fails.
DKIM sidesteps this cleanly. When the DKIM signature uses your domain (d=yourdomain.com), the signing domain and the From domain are the same, so alignment passes. This is why DKIM is the more reliable path to a DMARC pass for most shared-sending setups.
The most common cause of website email going to spam is that it is sent directly from the web server, without any authentication at all.
PHP’s built-in mail() function, and similar server-side mailers, simply hand the message to the local mail transfer agent and send it from the server’s IP address. That IP has no SPF record for your domain pointing to it, no DKIM key, and almost certainly no sending reputation.
From the perspective of the receiving server, the message is unauthenticated and comes from an unfamiliar IP. It will be junked or silently dropped. The site’s PHP log records a successful send, because PHP handed it off. The failure happens on the other side, invisibly.
The fix is to route all website email through a proper SMTP provider: Mailgun, Postmark, SendGrid, or a standard SMTP mailbox. The provider’s infrastructure has established sending reputation, and you publish SPF and DKIM records that authorise it to send on behalf of your domain. The message then has authentication the receiving server can verify.
status: sent. The messages were never reaching Gmail. On inspection, the sending subdomain had no SPF record at all and no DKIM key published. Every message was arriving unauthenticated from a shared hosting IP with no reputation. Switching to an SMTP provider and publishing both records resolved it. The log had been truthful all along: the messages left the server. They just could not get past the receiving end.
If you build on PageMotor, the EP Email plugin handles the sending layer. Configure it to use a real SMTP transport.
By default, PHP’s mailer is available but is not a reliable delivery route for the reasons above. EP Email should be pointed at a transactional email provider that gives you SPF and DKIM coverage.
Mailgun is the provider most commonly used in the ElmsPark ecosystem. The ElmsPark Mailgun guide covers the full setup: creating your Mailgun account, adding your domain, publishing the SPF and DKIM records Mailgun provides, and configuring EP Email to use the Mailgun SMTP credentials.
See: Setting up Mailgun email delivery for the step-by-step walkthrough.
A summary of each record, where it goes, and what it proves.
| Record | Where it goes | Type | Example value | What it proves |
|---|---|---|---|---|
| SPF | yourdomain.com |
TXT | v=spf1 include:mailgun.org -all |
This server is allowed to send for this domain |
| DKIM | selector._domainkey.yourdomain.com |
TXT (or CNAME) | v=DKIM1; k=rsa; p=MIGf... |
This message was signed by the domain owner and not altered |
| DMARC | _dmarc.yourdomain.com |
TXT | v=DMARC1; p=none; rua=mailto:[email protected] |
What to do when SPF or DKIM fails, and where to send reports |
The problems that actually come up, and what to do about each one.
This is almost always an alignment issue. SPF is passing for the provider’s domain (the envelope sender), not for your visible From domain. Set up DKIM with d=yourdomain.com in the signature and DMARC will pass via DKIM alignment instead. Most transactional email providers offer DKIM as part of their domain setup.
Check the selector. The DKIM record name must match the selector the provider uses when signing messages. If your provider signs with selector mg, the record must be at mg._domainkey.yourdomain.com. A mismatch means the receiving server fetches the wrong name and finds nothing. Also check for trailing whitespace or split-string issues if your registrar has line-length limits.
You have exceeded the 10-lookup limit. Count how many includes your SPF record pulls in, remembering that each include can itself pull in more. Remove providers you no longer use, or use a tool like MXToolbox to count your lookups. Some providers also offer a flattened SPF record (a list of IP addresses rather than includes) to stay under the limit.
Delete one. A domain must have exactly one SPF TXT record. Merge all your providers into a single record: v=spf1 include:mailgun.org include:sendgrid.net -all. While both records exist, every SPF check returns an error and every message is treated as unauthenticated.
The score factors in content as well as authentication. Common deductions include: HTML with no plain-text alternative, links to domains with a poor reputation, a From address on a domain different from the sending domain, and a subject line with spam-like patterns. The authentication checks are the critical part for deliverability; the content score is advisory.
If the rua address is on the same domain you are protecting, the reports should simply arrive (check your spam folder, since they come as XML attachments and are easily filtered). If the rua address is on a different domain, that domain has to opt in: it must publish a DMARC external-reporting authorisation record, a TXT record at yourdomain.com._report._dmarc.theirdomain.com with the value v=DMARC1. Without it, many receivers will refuse to send the reports. Free services like Postmark’s DMARC Digests or dmarcian can receive and parse reports for you.