A broken contact form not sending emails is one of the most frustrating website problems – mostly because there’s no visible error. Messages disappear silently. This guide walks through every real cause and exactly where to look.
Debugging a broken contact form is frustrating because the failure is usually invisible. You hit submit, the page says “Message sent!” – but nothing arrives. Or the page just reloads with no feedback at all. No error, no clue, nothing to Google.
The good news is that contact form failures have a small, well-defined list of causes. This guide goes through all of them – from HTML mistakes you can fix in two minutes to server configuration issues that need a different approach entirely.
HTML form issues – action, method, and name attributes
Before looking at the server, check the form HTML itself. Three missing or wrong attributes cause most basic form failures – and they’re easy to overlook because the form still renders perfectly on the page.
Missing or wrong action attribute
The action attribute tells the browser where to send the form data. If it’s missing, the form submits to the current page URL, which usually does nothing. If it points to the wrong file, the data goes nowhere.
|
1 2 3 4 5 6 |
<!-- Missing action - submits to current page URL --> <form method="POST"> <!-- Wrong filename - 404 silently --> <form action="send-mail.php" method="POST"> <!-- Actual file is: send_mail.php --> |
|
1 2 3 4 5 6 |
<form action="send_mail.php" method="POST"> <input type="text" name="name" required> <input type="email" name="email" required> <textarea name="message" required></textarea> <button type="submit">Send</button> </form> |
Missing name attributes on inputs
Every input that needs to be sent to the server must have a name attribute. Without it, that field’s data is never included in the form submission – your PHP or backend script receives nothing to work with, even though the user filled it in.
|
1 2 |
<input type="text" placeholder="Your name"> <!-- Missing name="" - browser won't include this in POST data --> |
|
1 |
<input type="text" name="name" placeholder="Your name"> |
Method is GET instead of POST
Using method="GET" appends form data to the URL as query parameters, which is fine for search forms but wrong for contact forms – it exposes data in the URL and often causes issues with the server-side handler expecting POST data.
Open DevTools ? Network tab ? submit the form ? click the request that appears. Under “Payload” or “Form Data” you can see exactly what was sent. If the fields you expect aren’t listed there, the name attributes are missing or the method is wrong.
Email landing in spam or junk folder
This is the most commonly overlooked cause. The form works perfectly – PHP sends the email, the mail server accepts it – but it arrives in the spam folder and you never see it. From your end, it looks like the form is broken.
Before anything else, check your spam folder. Also check any email filters or rules you’ve set up that might auto-archive or delete messages from unfamiliar senders.
Emails from contact forms get flagged as spam for several reasons:
- The
From:header uses a made-up email address that doesn’t match the sending domain - The server’s IP address is on a spam blacklist (common with shared hosting)
- No SPF, DKIM, or DMARC records are configured for the sending domain
- The email body contains words or patterns that trigger spam filters
- The server is sending from
localhostor a generic hostname rather than a proper domain
|
1 2 3 4 5 6 |
$headers .= "Reply-To: " . $userEmail . "\r\n"; $headers .= "Content-Type: text/plain; charset=UTF-8\r\n"; // Use Reply-To for the visitor's email // so replies go to them, not your no-reply address |
Setting
From: to the email address the visitor typed in is a common mistake. Spam filters flag this immediately because the email claims to be from [email protected] but is actually sent from your server. Always use your own domain in From: and put the visitor’s address in Reply-To:.
PHP mail() function not working on the host
The built-in PHP mail() function depends on the server having a configured mail transfer agent (like Sendmail or Postfix). Many modern hosting providers – especially shared hosts, VPS servers, and cloud platforms – have this disabled by default because it’s frequently abused for spam.
To find out whether mail() is available on your server, create a simple test file:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php // test-mail.php - upload to server and open in browser // DELETE this file after testing $sent = mail( 'Test from PHP mail()', 'If you see this, mail() is working.' ); echo $sent ? 'mail() returned true' : 'mail() returned false'; ?> |
If mail() returns false, or returns true but nothing arrives, the server isn’t configured for local mail sending. The reliable fix is to switch to SMTP – see the next section.
Most cloud VPS providers (DigitalOcean, Linode, AWS EC2), many shared hosts, and platforms like Heroku and Render don’t have local mail sending configured. This is increasingly standard – the solution everywhere is to use a dedicated SMTP service or email API instead.
Switch to SMTP – the reliable fix for most email problems
SMTP (Simple Mail Transfer Protocol) sends your contact form emails through a dedicated mail server rather than relying on the web server’s own mail setup. It’s more reliable, less likely to be flagged as spam, and works consistently across all hosting environments.
The most widely used PHP library for this is PHPMailer. Here’s a working example using Gmail’s SMTP:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception; require 'vendor/autoload.php'; $mail = new PHPMailer(true); try { $mail->isSMTP(); $mail->Host = 'smtp.gmail.com'; $mail->SMTPAuth = true; $mail->Password = 'your-app-password'; // Gmail App Password $mail->SMTPSecure = 'tls'; $mail->Port = 587; $mail->addReplyTo($_POST['email'], $_POST['name']); $mail->Subject = 'New contact form message'; $mail->Body = "Name: " . $_POST['name'] . "\n" . "Email: " . $_POST['email'] . "\n\n" . $_POST['message']; $mail->send(); echo 'Message sent successfully'; } catch (Exception $e) { echo "Error: {$mail->ErrorInfo}"; } ?> |
Gmail SMTP works well for low-volume contact forms (500 emails/day free limit). Other reliable options: Brevo (formerly Sendinblue) – 300 emails/day free; Mailgun – 5,000 emails/month free for 3 months; SendGrid – 100 emails/day free. All are significantly more reliable than PHP
mail().
Server-side errors silently swallowing the form
One of the most common reasons a contact form appears broken is that the PHP script has an error – but the error is hidden from view. The form submits, the page might even show a success message hardcoded in HTML, but the PHP never actually ran.
Temporarily turn on error reporting at the top of your PHP script to see what’s happening:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php // Add these two lines temporarily to see all errors ini_set('display_errors', 1); error_reporting(E_ALL); // Also check: did the form actually submit? if ($_SERVER['REQUEST_METHOD'] !== 'POST') { die('This page only handles POST requests'); } // Check that expected fields exist if (empty($_POST['email']) || empty($_POST['message'])) { die('Required fields are missing'); } // ... rest of your mail code ?> |
Error display should be off in production – it can expose sensitive server information. Use these lines only while debugging locally or in a staging environment, then remove them before the site is live.
Also check your server’s error log. On cPanel hosts it’s usually in public_html/error_log. On Linux servers: /var/log/apache2/error.log or /var/log/nginx/error.log. PHP errors that don’t show on screen almost always appear here.
JavaScript validation blocking submission
If your form uses JavaScript validation and there’s a bug in the validation script, it might silently prevent the form from ever submitting – even when all fields are correctly filled in. The form doesn’t send, no error appears, and it looks like the form is broken.
|
1 2 3 4 5 6 7 8 9 |
document.querySelector('#contact-form') .addEventListener('submit', function(e) { e.preventDefault(); // stops submission // Bug: emailFiled doesn't exist (typo) const email = emailFiled.value; // ReferenceError // Code never reaches form.submit() due to error above }); |
To test whether JavaScript is the problem, open DevTools ? Console and look for errors when you submit the form. Also try temporarily disabling all JavaScript on the page – if the form submits and works without JS, the JS validation is the culprit.
Always rely on server-side validation as your primary safety net, not just JavaScript. JavaScript validation improves user experience but can be bypassed, broken by script errors, or disabled entirely. Your PHP should validate required fields independently of whatever JavaScript does on the front end.
CSRF token mismatch or session issues
If your contact form uses CSRF (Cross-Site Request Forgery) protection – which it should – a token mismatch will silently reject the submission. This often happens when the page has been open for a long time (the session expires), when the form is cached and the token is stale, or when the session isn’t started before checking the token.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php session_start(); // Must be called before any session use // On form page: generate token if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } // In form HTML: // <input type="hidden" name="csrf_token" // value="<?= $_SESSION['csrf_token'] ?>"> // On submit: verify token if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) { die('Security check failed. Please refresh and try again.'); } // Regenerate after use unset($_SESSION['csrf_token']); |
If you’re seeing silent failures and your form has CSRF protection, temporarily add a visible error message when the token check fails so you can confirm whether this is the cause. Don’t remove CSRF protection – fix the token handling instead.
Third-party form services – common configuration mistakes
If you’re using a third-party service like Formspree, Netlify Forms, EmailJS, or similar, the HTML form itself works fine but the service isn’t delivering emails. These have their own set of failure points:
| Service | Common failure cause | Where to check |
|---|---|---|
| Formspree | Email not verified, or form endpoint URL wrong | Formspree dashboard ? Forms ? check submissions |
| Netlify Forms | Missing netlify attribute on the form tag |
Netlify dashboard ? Forms – does it appear there? |
| EmailJS | Wrong service ID, template ID, or public key | EmailJS dashboard ? Email Logs |
| Contact Form 7 (WP) | Mail settings wrong, or host blocks wp_mail() | Install WP Mail SMTP plugin for diagnostics |
| Any service | Free plan limit reached – submissions silently dropped | Check service dashboard for submission count vs limit |
Most third-party form services log every submission attempt, including failed ones. Before debugging your code, check the service dashboard – if submissions are appearing there but not arriving by email, the problem is between the service and your inbox (likely spam filtering or a wrong email address). If submissions aren’t appearing at all, the form isn’t reaching the service.
Step-by-step debugging checklist
Work through this in order when your contact form isn’t working:
- Check your spam folder first. Before debugging any code, look in spam and junk. This is the cause in a surprising number of cases and takes five seconds to rule out.
- Check the Network tab. Open DevTools ? Network ? submit the form. Does a POST request go out? What HTTP status does it return? A 404 means the action path is wrong. A 200 with no email means the server script ran but didn’t send.
- Verify the form HTML. Check that
actionpoints to the right file,method="POST"is set, and every field you need has anameattribute. Inspect the Network request payload to confirm the right data is being sent. - Enable PHP error reporting temporarily and submit the form. If PHP is erroring out, you’ll see the message. Check the server error log too.
- Test
mail()directly with a minimal test script. If it returns false or nothing arrives, your host has mail sending disabled – switch to SMTP. - Check the JavaScript console for errors during form submission. An uncaught error in a submit event listener can silently block the form from sending.
- Check the
From:header in your PHP mail code. It should be an address on your own domain, not the visitor’s email and not a made-up address. - If using a third-party service, check the service dashboard for submission logs before touching any code.
- Switch to SMTP if PHP
mail()isn’t reliable. PHPMailer with a free SMTP service is the most reliable solution for shared hosting environments.
Frequently Asked Questions
Q1. Why is my contact form not sending emails?
The most common causes are: the email landed in spam, PHP mail() is disabled on the host, the From: header is set incorrectly causing spam filtering, or there’s a PHP error silently blocking the script. Start by checking your spam folder and enabling PHP error reporting – those two steps resolve the majority of cases.
Q2.My form shows “Message sent!” but no email arrives. Why?
This usually means the success message is hardcoded in the HTML and displayed regardless of whether the email actually sent – or PHP mail() returned true but the server’s mail system discarded the message. Check your spam folder first. Then test mail() directly with a minimal script and switch to SMTP if it’s unreliable.
Q3.My contact form works locally but not on the live server. Why?
Your local machine almost certainly has a mail server configured that your live host doesn’t. Most shared hosting providers and cloud servers have PHP mail() disabled or unreliable. The fix is to use SMTP with a dedicated mail service like Gmail, Brevo, or Mailgun – this works consistently on any server.
Q4.How do I stop contact form emails going to spam?
Three things help most: set the From: header to a real address on your own domain (not the visitor’s email); use SMTP rather than PHP mail(); and make sure your domain has SPF and DKIM DNS records configured. Your hosting provider or domain registrar can help with SPF/DKIM – most have a guide for it.
Q5.My contact form submits but nothing happens at all – no success message, no error. Why?
Open DevTools ? Network tab and submit the form. If no POST request appears, a JavaScript event listener is calling e.preventDefault() and then erroring before the form submits – check the Console for JS errors. If a POST request goes out but the response is empty, the PHP script is likely erroring silently – enable error reporting to see what’s happening.
Q.What is the most reliable way to send contact form emails?
Use SMTP with a dedicated email service – not PHP mail(). PHPMailer is the standard PHP library for this and takes about 20 minutes to set up. For WordPress, the WP Mail SMTP plugin does the same. For simple forms without a backend, third-party services like Formspree or Netlify Forms are reliable and free for low volume.
The bottom line
A broken contact form almost always fails silently – that’s what makes it hard to debug. The most effective approach is to work through the pipeline: confirm the form data is being sent from the browser, confirm the server script is receiving and running it, confirm the mail function is actually sending, and confirm the email isn’t landing in spam. Each step narrows the problem to one specific place.
For most contact form email problems on shared hosting, switching from PHP mail() to SMTP with a free service is the fastest and most permanent fix. It takes about 20 minutes to set up and removes an entire category of problems permanently.
