---
title: "WordPress contact form emails not arriving? Fix it properly"
description: PHP mail() returns true when the message leaves your server, not when it arrives. Cause-ordered diagnostic for WordPress form mail, plus a 15-minute test.
url: "https://digitaldomination.xyz/en/blog/why-wordpress-contact-form-emails-vanish"
language: en-US
alternates:
  en-US: "https://digitaldomination.xyz/en/blog/why-wordpress-contact-form-emails-vanish"
  de-DE: "https://digitaldomination.xyz/blog/kontaktformular-mails-kommen-nicht-an-wordpress"
  x-default: "https://digitaldomination.xyz/blog/kontaktformular-mails-kommen-nicht-an-wordpress"
image: "https://digitaldomination.xyz/assets/images/og-image.en.png"
organization:
  name: Digital Domination
  telephone: "+49 1577 5527800"
  email: "hey@digitaldomination.xyz"
  address: "523 Jackson Street, Unit #210, Saint Paul, Minnesota 55101, US"
---

WordPress email deliverability SPF DKIM DMARC contact forms

# Why WordPress contact form emails vanish—and how to make them arrive

The cause-ordered diagnostic I actually run when a site owner tells me the inquiries stopped: From-header alignment, envelope sender, DKIM, host-level blocks, and the receiving side.

![Alexander Kirsch-Clayton](https://digitaldomination.xyz/assets/images/alexander.webp) By [Alexander Kirsch-Clayton](https://www.linkedin.com/in/alexanderkc/), Founder September 28, 2026 ·11 min read [Digital Domination on LinkedIn](https://www.linkedin.com/company/digidomination/) [Digital Domination on GitHub](https://github.com/digidomination)

A visitor fills in your contact form. The page says “Thank you.” They believe the message is on its way. Nobody at your company ever sees it—no bounce, no error, no entry in any log. The form is not broken. The mail path is.

In the audits I run, missing form mail shows up in three shapes. Nothing arrives at all, and the owner only finds out when a customer calls to ask why they were ignored. Or mail lands at Gmail but never at Outlook or GMX, so the problem looks like “one provider being difficult.” Or it is intermittent—four out of five inquiries arrive, the fifth does not, and everyone blames “the internet being weird.”

Every published answer to this question says “install an SMTP plugin.” That is sometimes the fix and almost never the diagnosis. Below is the order I work through, cheapest check first, plus a test protocol you can run in 15 minutes and a monitoring setup so the next failure is loud.

## Why the failure is silent

WordPress sends mail through `wp_mail()`, which by default hands the message to PHP’s `mail()`, which hands it to whatever mail daemon happens to run on the web server. That handoff is where the success signal comes from. `mail()` returns `true` once the local daemon accepts the message—not when a recipient’s server accepts it, and certainly not when a human reads it.

**PHP `mail()` returning true means the message left your web server, not that anyone received it.** That gap—no bounce, no error, no log—is where most missing inquiries live.

If the message is rejected two hops later, the bounce goes to the envelope sender, which on a default setup is something like `www-data@srv1234.hoster.tld`. That mailbox either does not exist or is read by nobody. So the rejection is real, documented, and invisible to you. The plugin’s log, if it has one, still says “sent.”

That is the whole reason this problem survives for months. Every other failure on a website announces itself. This one does not.

## Cause 1: the From-header lie

Most form plugins let you set the notification’s From address, and the most intuitive setting is the worst one: put the visitor’s address there. Now your web server sends a message claiming to be from `customer@gmail.com`.

The receiving server asks the obvious question—is this machine authorized to send as gmail.com?—and the answer is no. When both SPF and DKIM fail for the domain in the From line, Gmail answers with a `550-5.7.26` rejection, which SendLayer’s support write-up describes plainly as an unauthenticated-mail refusal rather than a judgment about your content. Rewriting the message text will not help. The problem is identity, not vocabulary.

The correct pattern is boring and it works:

- **From:** an address on a domain you control and are authorized to send from—`website@example.com`, not the visitor’s address.
- **Reply-To:** the visitor’s address, so hitting Reply still goes straight to the lead.
- **Subject and body:** carry the visitor’s name and address as text, so your CRM and your search still find them.

You lose nothing operationally. You stop asking Gmail, Microsoft and GMX to accept a forgery.

## Cause 2: the envelope sender nobody looks at

Fixing the visible From header fixes half the identity problem. SPF does not read that header at all. SPF is evaluated against the envelope sender—the Return-Path, the address given during the SMTP conversation—which MxToolbox describes as the address receiving servers use both for bounces and for the SPF lookup. It can be, and on default hosting usually is, completely different from what the recipient sees.

On shared hosting that envelope is typically the local system user at the hoster’s server hostname. SPF then passes beautifully—for the hoster’s domain. For yours it is not merely neutral, it is unaligned, and alignment is the thing DMARC checks. As Suped puts it, SPF authenticates only the envelope domain; authenticating the domain a human actually sees requires DMARC layered on top.

A healthy header block on a received test message looks roughly like this, with the same domain on the left of every line:

```
Authentication-Results: mx.google.com;
       spf=pass smtp.mailfrom=bounce@example.com;
       dkim=pass header.i=@example.com;
       dmarc=pass (p=NONE sp=NONE) header.from=example.com
```

If `smtp.mailfrom` ends in your hoster’s domain and `header.from` ends in yours, you have found your misalignment. That single line explains more missing mail than any plugin setting.

## Cause 3: no DKIM anywhere on the sending path

Mail sent straight out of a web server is normally unsigned. There is no DKIM key, no selector, nothing in the headers for a receiver to verify. Your mailbox provider signs the mail you send from your laptop; the website is a separate sending system and inherits none of that.

With no aligned SPF and no DKIM, DMARC cannot pass—it needs at least one of the two to pass *and* align. And here is the part that surprises people: if your domain publishes a DMARC policy of `p=quarantine` or `p=reject`, you have asked the world to junk or refuse mail that fails, and your own contact form is exactly such mail. The policy is doing its job. Your form is the unauthorized sender.

The fix is never “generate a key yourself.” You take the DKIM key your mail provider or relay generates, publish the record they give you at your DNS provider, and switch signing on in their dashboard. RFC 8301 sets 1024 bits as the floor and recommends 2048; some sending services only offer 1024, which is a vendor limit rather than something you misconfigured.

## Cause 4: the host blocks outbound mail on purpose

You can configure everything correctly and still send nothing, because the platform will not let the connection out. WP Engine states it directly in its own documentation: its infrastructure providers do not allow mail over port 25, so any SMTP solution has to use an alternate port or a provider API. Several other managed and shared hosts apply comparable restrictions—check your own host’s docs rather than assuming.

The symptom is distinctive. The plugin says “configured.” The test dialog spins and then times out, or reports a connection error with no detail. Confirming it takes about two minutes:

1. Search your host’s support docs for “outbound mail,” “port 25” or “SMTP.” Most hosts that block it say so on one page.
2. If you have SSH access, open a read-only connection test to your relay’s submission port: `openssl s_client -starttls smtp -connect smtp.example.net:587`. A banner means the path is open; a hang or refusal means it is not.
3. Switch the relay to its API transport instead of SMTP and re-test. If mail suddenly flows, the port was the problem.

Port 587 with credentials is open on most platforms that block 25. An HTTPS API is open on effectively all of them, which is why API transports have become the default recommendation on managed hosting.

## Cause 5: it is not always your fault

Sometimes your authentication is clean and the mail still crawls. Receiving providers have incidents. Exchange Online ran one tracked as EX1464935 starting August 31, 2026, with delays and failures sending and receiving mail plus authentication errors, which Microsoft traced to a common failure pattern in authentication and protocol connectivity and reported mitigated by September 3. A separate incident, EX1467029, opened September 4, 2026, specifically delaying mail to and from external domains, with Microsoft noting that anti-spam protections appeared to compound the problem for some accounts.

Add tenant-level transport rules, aggressive quarantine settings, and plain old junk folders, and a meaningful share of “your form is broken” tickets are actually “the recipient’s mail system did something.”

You can tell the two apart from one message. If you can open a received copy and the `Authentication-Results` line shows SPF, DKIM and DMARC passing on your domain, the sending side is doing its job and you should look at the recipient’s filters, rules and quarantine. If those checks fail, stop looking at the recipient—the problem is upstream in your own configuration, and no provider-side setting will paper over it.

## The architecture that actually works

Strip away the plugin marketing and there is one design that holds up:

- **An authenticated relay instead of `mail()`.** SMTP submission with credentials, or the relay’s HTTPS API. The point is not speed—it is that a relay gives you a real accept-or-reject answer and a log you can read.
- **From on a domain you control**, with one address reserved for the website, so you can tell at a glance whether a message came from the form or from a person.
- **Reply-To carrying the visitor.** Replying still reaches the lead in one click.
- **An envelope sender aligned with the From domain**, or at minimum a DKIM signature that is aligned, so DMARC has something to pass on.
- **One sending identity per system.** Website, newsletter tool, store, invoicing and CRM each get their own identity, so when something fails you know which system failed.

That last point is the one people skip and regret. If four systems share one identity, a deliverability problem is a guessing game. If each has its own, the DMARC reports tell you the answer.

## The DNS half

Before you touch anything, look at what is already published. These are read-only lookups:

```
dig +short TXT example.com
dig +short TXT _dmarc.example.com
dig +short TXT selector1._domainkey.example.com

# Windows
nslookup -type=TXT example.com
nslookup -type=TXT _dmarc.example.com
```

Copy the current value of every record before you change anything. Then work one record at a time.

**SPF.** A domain has exactly one `v=spf1` record. If your relay needs to be authorized, you *edit* the existing record to add its include—never publish a second one, because two SPF records is itself a failure. Watch the lookup budget: RFC 7208 caps the DNS lookups a single SPF evaluation may trigger at ten, counting everything inside nested includes, and going over returns a permerror that receivers treat as failure. It is invisible until you test for it. And do not move the record’s ending to `-all` until you have inventoried every system that sends as your domain.

**DKIM.** Publish the record your provider hands you, at the selector they specify, then confirm in their dashboard that signing is actually switched on for the domain—published-but-not-selected is a common half-finished state. Rotating a key later means adding the new selector, switching signing over, and removing the old record afterwards, never in one step.

**DMARC.** One `_dmarc` record, starting at `p=none` with a `rua=` address that a human actually reads. At `p=none` you are asking receivers for no specific action on mail that fails, while collecting reports on who is sending as your domain and whether they align—Sendmarc describes this monitoring phase as exactly what the data-collection stage is for, and equally notes that staying there forever leaves spoofing unaddressed.

Tightening to `p=quarantine` and then `p=reject` is a separate job, done after you have read the reports and fixed every legitimate sender—newsletter tool, store, CRM, invoicing, contact form. That ramp deserves its own article and its own change window. Do not jump it to fix a contact form.

## The 15-minute test protocol

Run this on the live site, not a staging copy—staging often has a different sending path.

1. Submit the real contact form three times, once each to a Gmail address, an Outlook or Microsoft 365 address, and a GMX or other European freemail address.
2. Open the received message at Gmail via “Show original,” at Outlook via message headers, and read the `Authentication-Results` line.
3. Confirm three passes: `spf=pass`, `dkim=pass`, `dmarc=pass`.
4. Confirm alignment: the domain in `header.from` matches the domain in `smtp.mailfrom` or in `header.i` for DKIM. Two passes on the hoster’s domain are not a pass for you.
5. Check where the message landed. Inbox, Promotions, Junk and quarantine are four different outcomes and only one of them is fine.
6. Send one submission to a mail-tester.com address and read the breakdown. It scores SPF, DKIM, DMARC, blocklist status and formatting from 0 to 10—useful as guidance, and its own documentation is clear that the score is not a delivery guarantee.
7. Enable a mail-logging plugin and submit once more. Verify the log records a real handoff to your relay, with a message ID, not just a local “sent” flag.
8. If any address never arrives, look at the relay’s own log next. A relay that accepted and then got a rejection will tell you the exact reason the web server could not.

Where an address is missing entirely and the relay has no record of it, you are back at Cause 4: nothing left the building.

## Monitoring, so the next failure is loud

Getting mail flowing today is the easy half. Keeping the failure from going silent again takes four small habits.

- **Mail logging with retention.** A log that keeps 30 to 90 days turns “I think someone contacted us in August” into a lookup.
- **DMARC aggregate reports, read monthly.** Ten minutes with the reports is how you notice a new sender or a newly failing one before a customer does.
- **A scheduled test submission.** Monthly, from an address outside your own organization, through the live form. Put it in the maintenance checklist next to plugin updates.
- **Provider-side data.** If you send meaningful volume, Google Postmaster Tools V2’s Deliverability analysis panel sits under Compliance status and translates volume, spam-rate, engagement and authentication signals into a plain verdict—useful proof that authentication is table stakes, not the finish line.

## What breaks it again

Form mail rarely degrades on its own. It breaks at specific moments, and every one of them is a reason to re-run the 15-minute protocol the same day:

1. A domain transfer to a new registrar.
2. A hosting move, or a DNS provider change—the zone gets recreated and one TXT record does not make the trip. Before you switch nameservers, confirm MX, SPF, every DKIM selector, DMARC and any verification TXT already exist at the new provider.
3. Adding a system that sends as your domain: newsletter tool, store, CRM, booking, invoicing.
4. A relaunch that migrates the site but not the mail configuration—new server, old plugin settings, no relay credentials.
5. A CDN or proxy change. In Cloudflare, mail hostnames stay DNS-only and are never proxied.

Every DNS edit also has timing. Caches serve the old answer until the TTL expires, so a change is never instant and never risk-free—keep the previous value so you can put it back.

## Questions I get asked every time

### Does installing an SMTP plugin fix this on its own?

Sometimes, and only when the missing piece was the transport. An SMTP plugin pointed at an authenticated relay replaces `mail()` and gives you real errors and logs. It does not fix a From header carrying the visitor’s address, it does not publish your SPF or DKIM records, and it cannot open a port your host has closed.

### Why does my form mail reach Gmail but not Outlook?

Receivers weight signals differently, so a marginal message passes one filter and fails another. Read the headers of the copy that did arrive: if SPF, DKIM and DMARC all pass and align, look at the recipient’s tenant rules, quarantine and junk folder. If they do not, you were borderline everywhere and one provider simply enforced first.

### Is it safe to put the visitor’s address in the From field?

It is not safe in the practical sense: you are sending mail that claims a domain you are not authorized to send from, and modern receivers refuse or junk exactly that. Use your own domain in From and the visitor’s address in Reply-To. Replies still reach them.

### Can my own DMARC policy block my own contact form?

Yes. If your domain publishes `p=quarantine` or `p=reject` and your website’s mail has neither aligned SPF nor aligned DKIM, receivers do what you asked and junk or refuse it. This is one of the most common causes of a form that “suddenly stopped working” months after a DMARC rollout that never inventoried the website as a sender.

### How do I prove an inquiry was never delivered?

With logs, which is why you enable them before you need them. A relay log with a message ID and a rejection reason is evidence. A form plugin log that only says “sent” is not, because it is recording the local handoff described at the top of this article, not delivery.

## What to do this week

Submit your own form to three different providers and read one header block. That is 15 minutes and it either ends the worry or gives you the exact line to fix. If it turns up misalignment and you would rather not spend a weekend on DNS, we run this check as part of [Website Maintenance](https://digitaldomination.xyz/en/services)—or just [send us the header](https://digitaldomination.xyz/en/contact) and we will tell you which of the five causes you are looking at.

## Sources

1. [Why wp_mail() silently eats your form notifications — and how to fix it for free](https://dev.to/mrpsiho/why-wpmail-silently-eats-your-form-notifications-and-how-to-fix-it-for-free-5ckl) (accessed 2026-09-28)
2. [Error 550-5.7.26: This Mail Is Unauthenticated — SendLayer](https://sendlayer.com/docs/error-550-5-7-26-this-mail-is-unauthenticated/) (accessed 2026-09-28)
3. [What Is an Email Return-Path? — MxToolbox](https://mxtoolbox.com/dmarc/spf/setup/spf-return-path) (accessed 2026-09-28)
4. [How to Send Email on WP Engine — WP Engine Support](https://wpengine.com/support/using-3rd-party-email-provider-send-mail-wordpress/) (accessed 2026-09-28)
5. [When SPF Lookup Limits Actually Break Things — DuoCircle](https://www.duocircle.com/blog/when-spf-lookup-limits-actually-break-things/) (accessed 2026-09-28)
6. [DMARC Aggregate Reports: What They Reveal — Sendmarc](https://sendmarc.com/dmarc/aggregate-reports/) (accessed 2026-09-28)
7. [How to use mail-tester.com to check email deliverability — mybox.com Help](https://mybox.com/help/en/knowledgebase/mail-tester-com-evaluating-and-interpreting-your-email-quality/) (accessed 2026-09-28)
8. [Ultimate Guide to Google Postmaster Tools V2 — Suped](https://www.suped.com/blog/ultimate-guide-to-google-postmaster-tools-v2) (accessed 2026-09-28)
9. [Massive Microsoft 365 outage causes auth issues, service failures — BleepingComputer](https://www.bleepingcomputer.com/news/microsoft/microsoft-exchange-online-outage-causes-email-failures-auth-issues/) (accessed 2026-09-28)
10. [Microsoft Confirms New Exchange Online Outage Delaying Emails from External Domains — Cyber Security News](https://cybersecuritynews.com/exchange-online-outage-delaying-emails/) (accessed 2026-09-28)
11. [Against which domain is SPF checked? — Suped](https://www.suped.com/learn/spf/against-which-domain-is-spf-checked) (accessed 2026-09-28)
