Skip to content

Your custom contact form is not sending email

Hand-built forms fail in the quietest way of all. There is no plugin dashboard listing failed entries and no vendor status page — the endpoint returns 200, the page says thank you, and the message is gone.

Check your site now

See whether a visitor can actually complete your form today.

Free · No signup · We never submit your form

Why this happens

01

The mail call succeeds but the message is rejected downstream

PHP's mail() and most minimal SMTP wrappers return success once the message is handed off. If the receiving server then rejects it on SPF, DKIM or DMARC — very common when the From address is the visitor's own email rather than your domain — the bounce goes nowhere your code can see.

02

Expired or rotated SMTP credentials

Transactional providers rotate API keys, and app passwords get revoked when someone enables 2FA. The send now fails, but if the failure is caught and swallowed the form still returns its success response.

03

The error is caught and hidden

try/catch around the send, with the catch logging quietly or doing nothing, is the single most common cause. The visitor's experience is indistinguishable from success.

04

The form action points at a route that no longer exists

After a framework migration or a route rename, the POST can hit a catch-all that returns 200 without ever reaching the handler.

05

Spam protection rejecting real people

Honeypot fields that autofill, rate limits keyed too tightly, or a reCAPTCHA v3 score threshold set too high will discard genuine submissions silently, by design.

How to check it yourself

  1. 01Submit the form yourself from a phone, on mobile data rather than office wifi.
  2. 02Check the mail server logs for the exact timestamp, not just the application logs.
  3. 03Confirm the From address is on your own domain and that SPF and DMARC pass for it.
  4. 04Look in spam and quarantine for the receiving mailbox, including any shared alias.
  5. 05Verify the endpoint the form posts to actually reaches your handler and is not absorbed by a catch-all route.

The part you cannot check once

Fixing it is the easy half. Knowing it stays fixed is the hard half.

Every cause above can come back without anyone touching the page. API keys rotate, plugins update, DNS records get edited, licences lapse. The form still renders, still accepts the enquiry, still says thank you — and nothing arrives.

FormWatcher submits an identifiable test enquiry on a schedule and confirms it actually reaches your inbox. If delivery stops, you hear about it — rather than working it out from a quiet month.

Common questions

The endpoint returns 200. Does that mean the email was sent?
No. A 200 means the request was accepted. It says nothing about whether a message left your server, or whether the receiving server accepted it. This gap is where most silent form failures live.
How can I tell the difference between a broken form and a broken mailbox?
Submit a uniquely identifiable test enquiry, then look for that exact marker at the destination. If it never arrives, the failure is in delivery rather than in the form itself.
Can this be monitored without adding code to my site?
Yes. FormWatcher tests the journey from the outside, the way a customer experiences it, and needs nothing installed.

Related problems

All form delivery problems →