Contact form emails going to spam
Contact form emails going to spam
Your contact form isn’t broken. The notifications it sends are generated by the web server itself, with no SPF authorisation for the IP they leave from and no DKIM signature on the message, and receivers treat that the way they treat any unauthenticated mail from an unknown box. Route the site’s mail through an authenticated SMTP account at your own domain and the same form starts landing in the inbox.
Who is sending your form mail
When a visitor submits the form, your mail provider never sees the message. The web server builds it and hands it to the local mail transport, on most stacks through PHP mail(). As far as the receiving side can tell, the sender is the web box.
Three things follow from that. The connection arrives from an IP your SPF record doesn’t authorise, because SPF covers the mail provider and says nothing about the web host. The message carries no DKIM signature, so nothing cryptographically ties it to your domain. And on shared hosting the IP’s reputation is set by the hundreds of other sites on the same server, none of which you chose.
That’s the profile spam filters exist to catch. Open the headers on one of the junked notifications and the Authentication-Results line spells it out: spf=fail or spf=none, and no dkim= entry naming your domain at all.
The wider picture explains why receivers stopped giving unauthenticated mail the benefit of the doubt. In the September 2026 census, 166,497,698 (52.6%) of the 316,600,902 domains graded or found dead publish no SPF record at all, and of the 75,823,110 domains where the DKIM check ran, 37,823,648 (49.9%) showed no selector. Against that background, a server nobody vouches for gets no charity.
Why WordPress sites get this in bulk
WordPress sends all of its email through one function, wp_mail(), and by default that function wraps PHP mail() on the web server. Form notifications, password resets and order confirmations take the same route, so a fresh WordPress install is an unauthenticated sender out of the box.
Form plugins don’t change this. Whichever one you’ve installed, it composes the message and hands it to wp_mail() like everything else. Swapping one form plugin for another moves the problem to a different settings page and leaves the transport where it was.
The remedy is an SMTP plugin. It reroutes everything wp_mail() sends through a real, authenticated mail account, which is infrastructure your SPF record already authorises and which attaches a DKIM signature on the way out. Because it sits underneath wp_mail(), the one change fixes the password-reset and order emails in the same move as the form.
Four ways the site can send, side by side
| Sending method | SPF | DKIM | Survives a host migration | Verdict |
|---|---|---|---|---|
PHP mail() / default wp_mail() from the web box | fails | none | broken everywhere | the problem |
| Authenticated SMTP through the mailbox provider you already pay for | passes | signed | yes | the fix for a few notifications a day |
| A transactional email service with your sending domain verified | passes | signed | yes | the fix at volume |
| The web server’s IP added to your SPF record | passes (SPF only) | none | no, it breaks silently when the IP changes | fallback only |
For a handful of enquiries a day, SMTP through the mailbox you already have is the shortest path. At real volume, an online shop or a form that fires hundreds of times a day, a transactional service is built for it. Both give you a signature. The IP shortcut doesn’t, and the section on it below explains why that costs more than the SPF column suggests.
The fix, in order
- Scan the domain first. The free scan shows what SPF, DKIM and DMARC your domain publishes today, so you know if you’re fixing the form’s transport, a missing record, or both. If there’s no SPF at all, the SPF pillar covers building the record before you route anything through it.
- Create a dedicated sending identity for the site, something like
[email protected]in your mailbox provider, or a verified sending domain in a transactional service. Give it an app password or API key you can revoke. Don’t put a person’s mailbox password into a plugin settings page. - Point the site’s mailer at it. On WordPress, install an SMTP plugin and enter that account. On other stacks, configure the framework’s mailer instead of local
mail(). - Fix the From address on the notification. From must be an address at your own domain; the visitor’s address goes in Reply-To. The next section covers why.
- If a transactional service sends for you, publish its DKIM records, which arrive as a pair of CNAMEs, so the mail is signed with your domain rather than the service’s. The DKIM pillar explains what a selector is and how to confirm the record resolves.
- Send a test submission and read the headers.
Authentication-Resultsshould showspf=passanddkim=passwith your domain named beside both. Then re-scan and clear anything else the report flags.
The plugin doesn’t replace the records. It changes which infrastructure sends the mail, while SPF and DKIM are what authorise that infrastructure to send as you. Authenticated SMTP on a domain with no SPF record moves the failure rather than removing it.
From is your domain, Reply-To is the visitor
Some form setups, and some plugins by default, set the notification’s From to the visitor’s own address so the owner can hit reply. It’s forgery, and receivers treat it as such.
Your server has no right to send mail as [email protected]. The message fails SPF and DKIM for the visitor’s domain, and the visitor’s provider publishes a DMARC policy telling receivers what to do with mail that fails both, which is to junk it or reject it. The enquiry gets filtered because of who it claimed to be from, before your own domain’s records come into it.
The correction is two fields in the notification settings. From is an address at your own domain, the sending identity from step 2. Reply-To is the visitor’s address. Hitting reply still goes straight to them, and the message now claims an identity your infrastructure can prove.
Adding the server’s IP to SPF, and why it’s a fallback
Adding ip4:<server> (or an a mechanism pointing at your web host) to the SPF record makes the raw check pass. It’s the wrong first move for three reasons.
On shared hosting the IP belongs to the box. Every other site on that server can now send SPF-passing mail as your domain, and you’ve authorised them without knowing their names.
It breaks silently. Hosts migrate servers and rotate IPs without telling you, and the record keeps authorising an address you left months ago while your form mail goes back to failing from wherever it lives now.
It gives you no DKIM. SPF alone breaks under forwarding, and it can’t carry you to DMARC enforcement the way an aligned signature can. You’d be spending one of your ten lookups on a mechanism that gets you halfway.
Reserve this route for the constrained case: a dedicated server with a static IP you control, running an application that can’t speak SMTP. If that’s you, pair it with DKIM signing on the box so the mail leaves with a signature anyway.
Where SPF looks, and why the web box was never yours
Receivers evaluate SPF against the Return-Path domain, the RFC5321.MailFrom address on the envelope. The From header the visitor sees plays no part in that check.
Web servers stamp their own hostname onto the Return-Path. So when one of those junked notifications went out, the receiver checked SPF for something like srv0421.examplehost.com, and your record wasn’t consulted. Whatever you’d published for your own domain sat unread while a hosting company’s hostname took the test on your behalf.
Authenticated SMTP changes the Return-Path to your domain, so the SPF pass finally counts for you. DMARC alignment then needs a pass tied to the domain in the From header, and that’s what a DKIM signature travels with: it’s attached to the message, it names your domain, and it survives the hop through whatever relay sits between the site and the recipient. The email delivery pillar covers how the three checks fit together once the site is sending properly.
Send the test submission, read the two pass results with your domain beside them, and the enquiries come back.
Figures as of 5 September 2026, from the September 2026 edition of the defaults.exposed census. Census numbers move every month; the current values are on the census data page.