Deliverability
Why your form emails land in spam
Form notifications in the spam folder is a deliverability problem, not a forms plugin problem. Here's the four-step fix that actually works.
Client message at 9 AM: “I haven’t been getting any form notifications. Are leads coming in?”
Open the database. 47 submissions over two weeks. All of them sent notifications. None of them landed in the inbox. All of them in Gmail’s spam folder, marked “spoof attempt detected.”
This is the most common email failure on WordPress sites. It’s almost never the forms plugin’s fault. It’s the SPF, DKIM, and DMARC story being broken.
Here’s the four-step fix.
Why this happens
WordPress’s wp_mail() defaults to PHP’s mail() function. That sends through your web server’s local sendmail. The “From” address is whatever you configure (usually wordpress@yourdomain.com).
Three things go wrong simultaneously:
-
The web server’s IP doesn’t match the domain’s SPF record. Gmail sees an email “from yourdomain.com” sent from an IP that yourdomain.com hasn’t authorized. Auto-spam.
-
No DKIM signature. Modern receivers (Gmail, Outlook, Yahoo) want DKIM. PHP
mail()doesn’t sign messages. Auto-spam-suspicion. -
Sender address mismatch. The form has the visitor’s email in the “From” field (worst pattern). Gmail sees an email “from random@gmail.com” hitting their own servers from a third-party WordPress install. Auto-spoof-detection.
Fix all three, deliverability goes from 50% to 99%.
Step 1: Stop using the visitor’s email as the sender
The most common mistake. The form notification is “From” the visitor.
From: jane@example.com
To: you@yourdomain.com
Subject: New contact form submission
This is broken. You’re claiming to send from a Gmail address, from your WordPress server. Gmail flags it as spoofing.
Fix: send from your own domain. Use the visitor’s email in the Reply-To header instead.
From: hello@yourdomain.com
Reply-To: jane@example.com
To: you@yourdomain.com
Subject: New contact form submission from Jane
Now you’re sending from your domain (which you control), and the reply still goes to the visitor.
In Core Forms, the email action has separate From and Reply-To fields. Set From to your domain, Reply-To to [email] (the form field). Done.
Step 2: Use a real SMTP provider
PHP mail() is the lazy default. It works on a developer’s local machine. It fails in production.
Pick one of:
- Emailit — Built into Core Forms, no extra plugin. Cheap. EU-friendly.
- Postmark — Best deliverability of any I’ve tested. ~$15/month for 10,000 emails.
- SendGrid / Mailgun / SES — All viable. SES is cheapest at scale.
Plug your SMTP credentials into a transactional plugin (WP Mail SMTP works) or use the Emailit action directly. Either way, your email leaves the server through an authenticated SMTP connection, signed and tracked.
I default to Emailit on most client sites because it’s bundled with Core Forms. No extra plugin, no extra account to maintain.
Step 3: Set up SPF, DKIM, and DMARC
The three DNS records modern email needs.
SPF. A TXT record on your domain that lists which IPs/services are allowed to send email “from” your domain. Whatever SMTP provider you picked in Step 2 will give you the SPF entry to add.
For Postmark, it looks like:
v=spf1 a mx include:spf.mtasv.net ~all
DKIM. A cryptographic signature on outgoing emails. Your SMTP provider generates a public key, you publish it as a CNAME record. The provider signs every email with the matching private key. Receivers verify the signature.
Each provider gives you 1-3 DNS records to add. Postmark’s wizard walks you through it. Emailit’s setup wizard does too.
DMARC. A policy that says what receivers should do when SPF or DKIM fails. The starter record:
v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
p=none means “monitor only, don’t reject.” That’s the right starting point. After two weeks of clean delivery, change to p=quarantine. After two more weeks, p=reject if you’re confident.
Step 4: Use Core Forms’ email log
Even with all of the above set up, you’ll occasionally see weird failures. Gmail tightening rules. Postmark suspending an account on bounce volume. A typo in a DNS record.
The Core Forms Email logs view (Settings → Email logs) records every outgoing email with its status. Sent, queued, failed, bounced. When a client says “I’m not getting form notifications,” check the email log first. It tells you whether the email left the server, whether the SMTP provider accepted it, and whether the receiver bounced it.
Most of the “missing email” tickets I get are answered by reading the email log for 30 seconds.
What you should not do
Don’t use Gmail’s SMTP for transactional email. Gmail’s daily send limits will catch up to you. Their authentication is also tied to your personal Google account, which is a bad coupling.
Don’t trust “free” SMTP plugins that route through their own servers. If a plugin says “we’ll send your email for you,” they’re rate-limiting you, branding the emails, or both. Use your own SMTP credentials.
Don’t skip DMARC because “it’s complex.” DMARC at p=none is just monitoring. It doesn’t reject anything. It tells you whether you have a problem.
Don’t email-blast subscribers from your transactional SMTP. Postmark, Emailit, SES — all of them will throttle or terminate you for sending newsletters. Use a separate ESP for marketing. Mailchimp, MailerPress, or whichever fits.
The thirty-second test
Send a form submission to yourself. Then check:
1. Did it arrive?
2. Did it land in inbox or spam?
3. Open the email, view full headers (Gmail: "Show original").
4. Look for "spf=pass", "dkim=pass", "dmarc=pass".
If any of those say “fail” or “neutral,” that’s the bug.
The next step
Pick one client site. Run the test above. Find the failing piece. Fix it.
Once SPF, DKIM, and DMARC all pass on a clean SMTP provider, form-notification deliverability stops being a recurring support ticket.
Email logs and Emailit are bundled in Core Forms. Pricing.