Contact Form Not Sending Email: The Outage That Reported Itself as Successful
A client came to us on a Friday morning with an urgent problem: their website’s inquiry form had stopped emailing them. New leads were still appearing in their database, so the form itself was clearly working, but nothing was reaching anyone’s inbox. They had run their own test and received nothing. For a business that takes its enquiries almost entirely through that one form, this was a revenue outage rather than a bug report. We had it diagnosed, rebuilt, and verified before lunch.
The Problem
The site was a long-lived PHP application on shared hosting, with three separate enquiry forms all feeding a single handler. That handler did two things on submission: write the lead to a MySQL database, then email an alert to the team.
The database half was working perfectly. Every lead was there. The email half was producing nothing at all, with no error anywhere to explain it.
Worse, the system was actively reporting success. The application recorded a delivery status against every lead, and every single record said the email had been sent.
The Investigation
What the send log actually showed
We started with the send log, and it immediately reframed the problem. Across roughly 1,800 submissions going back months, PHP’s mail() function had returned true every single time. Not one recorded failure, including the client’s own test.
That was the key. mail() does not report delivery. It reports only that the local mail server accepted the message for later processing. It returns true and moves on regardless of what happens next.
So the fault was not in the application. It sat somewhere between the web server and the inbox, and the code had no visibility into that gap.
Mapping the mail path
Mapping the path explained why. The website ran on shared hosting, but the company’s mailboxes were on Google Workspace. Every lead alert was therefore being sent as the company’s own domain, to the company’s own domain, from a machine that was not Google. We also found the domain’s sender authentication was permissive and incomplete, giving Google no strong signal to trust those messages.
That is a fragile arrangement at the best of times. Whether the message was being discarded before it left the host or filtered on arrival, the outcome for the client was identical: silence.
Why nobody caught it sooner
The delivery-status field in the database was storing the return value of mail(), the very thing that was always true. The monitoring was faithfully reporting the health of the wrong thing.
The Solution
We stopped trying to identify which downstream component was eating the mail and removed the dependency instead. If the message never has to cross from the web host into Google, neither possible cause can apply.
We rebuilt delivery on authenticated SMTP straight to Google Workspace, using the PHPMailer library already present in the project. Messages now originate inside the client’s own mail environment and are delivered internally.
Three things mattered in how we shipped it:
- A safe rollout. The new path falls back to the old method automatically if credentials are absent, so the live form could not break part-way through deployment.
- Honest reporting. Failures now return the actual SMTP error, which is written to both the log and the lead record. The delivery-status field finally means something.
- A second defect removed. While reading the handler we found a stray send outside its intended condition, firing on every submission with malformed headers and carrying internal lead data to the visitor’s address. It had been running unnoticed on every enquiry.
The Result
The first live submission after deployment confirmed the fix two ways. The log showed a clean send with no fallback marker, meaning the authenticated path had been used. And the timestamps showed a two-second gap between composing and sending, the round trip of a real SMTP conversation. The old mail() call returned instantly, which is precisely why it could never tell anyone anything useful.
- ✅ Reported before 9am, resolved and verified by 11:30 the same morning
- ✅ Client confirmed receipt within minutes of deployment
- ✅ Delivery now runs on authenticated SMTP inside the client’s own mail environment
- ✅ Failures report the real SMTP error instead of a meaningless success flag
- ✅ A second, unrelated defect leaking internal lead data to visitors was found and removed
- ✅ Every enquiry received during the outage recovered from the database
Nothing was lost. The leads simply never reached anyone. We provided the backlog for follow-up, with one warning: the historical delivery-status column could not be trusted, because it had been recording success for messages that never arrived.
The Takeaway
“Sent” is not “delivered.” PHP’s mail() confirms handoff, not receipt. Any system that treats its return value as proof of delivery is not monitoring anything.
Split hosting is where mail quietly breaks. When your website and your mailboxes live with different providers, sending as your own domain from your web server is inherently brittle. Authenticate to the provider that actually holds the mailboxes.
Record outcomes, not return values. A status field that can only ever say “success” is worse than no status field, because it manufactures false confidence.
The failure mode to fear is the silent one. This outage was not caught by logs, alerts, or dashboards. It was caught by a customer noticing. That is the case for monitoring that tests real delivery rather than trusting the application’s own account of itself.
This was a custom PHP application, but the same trap is waiting in most WordPress sites. WordPress sends through PHPMailer, and unless SMTP has been configured deliberately, it falls back to the same mail() transport with the same blind spot. A contact form that reports success while nothing arrives looks identical on either stack, which is one reason form delivery is worth testing as part of routine maintenance rather than assumed.
Is your contact form quietly failing?
If enquiries have gone quiet, or you cannot tell whether your form emails are actually arriving, Webnorly can trace the real delivery path and move you onto authenticated sending that reports honestly when something fails. Send us the details and we will find out whether your form is delivering or only claiming to.
Related case studies
Three Backdoors, Zero Alerts: Finding WordPress Malware a Security Plugin Missed
Booking forms failed, the page builder would not load, and the error log showed nothing. The site's security plugin reported no problems. Verifying every file against its official source found three backdoor files, each written specifically to survive the scan that had already cleared the site.
Read articleWordPress Site Loading Without CSS, JavaScript, or Images: A File Permission Fix
A WordPress website can look completely broken while its pages, database, and admin area are all still working. This site was loading as mostly unformatted HTML: the layout gone, images broken, navigation reduced to plain links, and parts of the dashboard unstyled. The cause was not malware, and not a failed update. It was a […]
Read articleRestoring a Legacy WordPress Website After PHP Compatibility Errors
A legacy WordPress site built with an older theme, bundled shortcodes, dated builder components, and outdated plugins started breaking after a PHP update. We restored admin access, resolved critical fatal errors, handled plugin and builder conflicts, and recovered the broken promo box shortcode section while preserving the existing design.
Read article