Contact Form Not Sending Email? A Troubleshooting Checklist
Contact form not sending email? Work through this checklist: broken submits, missing name attributes, PHP mail() limits, SPF/DKIM, spam folders and wp_mail.
7 min read

When a contact form is not sending email, the problem is in one of three places: the submission never reaches your server, your server can't send mail, or the email is sent and then filtered before it reaches your inbox. Testing each stage in order, from browser to server to inbox, tells you which one it is, usually in under half an hour.
Here's the full checklist, with a test and a fix for each step. Start at the top. There's no point debugging SPF records if the form never submits.
Stage 1: Does the form actually submit?
Missing or wrong name attributes
Browsers only send fields that have a name. An input with only an id is silently left out, so the handler receives an empty message, or rejects it as incomplete.
<!-- Wrong: never submitted -->
<input type="email" id="email">
<!-- Right -->
<input type="email" id="email" name="email">Test: open DevTools → Network, submit the form and click the request. Check the payload lists every field with a value.
Fix: give every input, select and textarea a name. The handler expects specific names, so match them exactly. email and Email are different fields.
Wrong action or method
If action points at a file that doesn't exist, you'll see a 404. If method is missing, the browser sends a GET with the data in the URL, and most mail handlers ignore it.
Test: check the request URL and status code in the Network tab. A 404, 405 or 500 tells you the problem is on the server side of the request, not email.
Fix: use method="POST" and an action that resolves to your handler. Our guide to the HTML form action attribute explains how action, method and enctype fit together.
JavaScript errors and preventDefault
Many forms submit with fetch and call event.preventDefault(). If the script throws before the request is sent, the page shows nothing, or a thank-you message, and no request goes out.
Test: open the Console tab and submit. Any red error before the request means the script is the problem. Also check the Network tab: no request at all means the data never left the browser.
Fix: fix the error, and only show a success message after the response comes back OK. Our guide to submitting a form with JavaScript shows a pattern that handles errors properly.
A mailto: action
action="mailto:you@example.com" doesn't send email. It asks the visitor's computer to open a mail app, which many people don't have configured. If that's your setup, nothing is broken. It just never worked reliably.
Stage 2: Can the server send mail?
If the request reaches your server and returns a success status, the next question is whether the server can send email at all.
PHP mail() disabled or restricted
Many PHP contact forms rely on mail(). Plenty of hosts disable it, rate-limit it or only allow it from specific addresses, because it's widely abused for spam.
Test: upload a minimal script and load it in the browser:
<?php
$ok = mail('you@yourdomain.com', 'mail() test', 'If you can read this, mail() works.', 'From: website@yourdomain.com');
var_dump($ok);bool(false) means PHP couldn't hand the message to the local mail program. bool(true) only means the handoff worked, not that the email was delivered. Delete the script when you're done.
Fix: check your host's documentation or support. Often the answer is to stop using mail() and send through an authenticated SMTP account instead. A library like PHPMailer makes that straightforward. If you'd rather avoid server-side code completely, see how to build a contact form without PHP.
Blocked SMTP ports
Cloud servers and some hosts block outbound port 25, and sometimes 465 and 587 too, to stop spam. Your SMTP connection then times out.
Test: check your application's error log for connection timeouts, or run nc -vz smtp.your-provider.com 587 from the server.
Fix: use the port your provider recommends (usually 587 with STARTTLS), ask your host to open it, or use a provider that offers an HTTP sending API.
WordPress wp_mail
WordPress sends every email, including contact form plugin notifications, through wp_mail, which uses PHP mail() unless something overrides it. So a WordPress contact form that doesn't send email usually has a server mail problem, not a plugin problem.
Test: reset your password from the login page. If that email doesn't arrive either, the problem is site-wide. Many SMTP plugins also include a "send test email" tool and an email log.
Fix: install an SMTP plugin and connect it to an authenticated email provider. Then check the plugin's form settings: a wrong "To" address, or a "From" address set to the visitor's email, is common.
Stage 3: Is the email being sent but filtered?
If the server says the email was sent but nothing shows up, the message is being filtered or rejected on the way to you.
Check spam, quarantine and filters
Look in spam, junk, the Promotions tab and any admin quarantine (Microsoft 365 and Google Workspace both have one). Check for inbox rules or forwarding that moves messages out of sight.
Fix: mark the message as "Not spam", add the sender to your contacts and create a filter that always delivers it.
The From address spoofs the visitor
Scripts that set From: to whatever the visitor typed make your server claim to be Gmail, Outlook or someone's company domain. Receiving servers check whether your server is allowed to send for that domain. It isn't, so the message is filtered or rejected.
Fix: send from a fixed address on your own domain and put the visitor in Reply-To:
From: Website <forms@yourdomain.com>
Reply-To: jane@example.comMissing SPF, DKIM or DMARC
Even with the right From address, mail from a server your DNS doesn't authorize fails authentication. Major mailbox providers expect authenticated senders and filter, or reject, mail that isn't.
Test: open a notification that did arrive (or one in spam) and view the raw headers. In Gmail, that's "Show original". Look at the SPF, DKIM and DMARC results. Online DNS lookup tools will also show which records you've published.
Fix: publish one SPF record that includes your sending service, enable DKIM signing with your provider, and add a DMARC record starting at p=none. Our post on contact form emails going to spam covers these records step by step.
Rate limits and provider blocks
Hosts often cap outgoing mail per hour. A burst of bot submissions can use up that cap, or get your shared IP listed on a blocklist, so real messages stop going out. Corporate mail gateways may also reject mail from IPs with a poor reputation.
Test: check the host's mail logs or your email provider's activity log for "deferred", "rate limited" or "rejected" entries. Look up your server's IP on a public blocklist checker.
Fix: stop the spam at the form with a honeypot or captcha (see how to stop form spam), move sending to a transactional email provider, and ask your host to request delisting if your IP is on a blocklist.
The checklist at a glance
| Symptom | Likely cause | First test |
|---|---|---|
| No request in the Network tab | JavaScript error, mailto: action | Console errors |
| Request sent, fields empty | Missing name attributes | Request payload |
| 404 / 405 / 500 response | Wrong action or method, handler error | Status code, server error log |
mail() returns false | PHP mail disabled | Minimal test script |
| SMTP timeouts | Blocked ports | Error log, nc to port 587 |
| Sent but never arrives | Spoofed From, no SPF/DKIM | Raw headers |
| Worked, then stopped | Rate limit or blocklist after spam | Mail logs, blocklist lookup |
How a form backend avoids most of this
Almost every item in stages 2 and 3 comes from running your own mail sending: mail() settings, SMTP ports, DNS records and IP reputation. A hosted form backend removes that layer. The form posts to an endpoint, and the backend stores the submission and sends the notification from a sending domain it authenticates and maintains.
With FormSubmit, the change is the form's action:
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
<input type="text" name="name" required>
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
<button type="submit">Send</button>
</form>What that changes in practice:
- Notifications come from
notifications@formsubmit.app, with Reply-To set to the visitor, so you can reply directly and nothing claims to be the visitor's domain. - Every submission is stored in the dashboard, so even if an email is filtered, the message isn't lost.
- Spam is filtered before emails go out, with a honeypot, timing checks, content heuristics and rate limiting, so bots don't burn through sending limits.
- No PHP, SMTP or DNS work on your host. It works on static hosting, site builders and WordPress alike.
Stage 1 still applies: fields need name attributes and JavaScript has to send the request. But that's quick to check. To set it up from scratch, follow our guide to sending an HTML form to email, or build one in the form generator. The free plan includes one form and 50 submissions a month. Add notifications@formsubmit.app to your contacts on day one.


