Building Contact Forms That Actually Work (with Anti-Spam)

Because ‘message sent’ should actually mean message received.

Published: January 2025 • 8 min read

Why Most Contact Forms Are Broken

We’ve all filled out contact forms that never result in a reply. Worse, we’ve all had inboxes flooded with spam from our own sites. Whether it's bots bypassing validation or poor email headers throwing legitimate messages into spam folders — most contact forms are either ineffective or downright risky.

This guide walks through how to build a form that actually works. That means spam protection, proper server-side validation, and emails that reliably land in your inbox — not the junk folder.

What You'll Learn

  • How to integrate Google reCAPTCHA v3 correctly
  • How to validate input on the server (not just in the browser)
  • How to use AJAX without breaking accessibility
  • Simple techniques to block spam with keyword filters and rate limits
  • How to craft proper email headers so replies don’t vanish

Google reCAPTCHA v3

Google’s reCAPTCHA v3 is invisible and assigns a score to each visitor based on behaviour. You can use this score to decide whether to process or silently reject a submission.

if (recaptcha_score < 0.5) {
    exit; // suspicious behaviour
}

Server-Side Validation

Client-side validation is useful for UX, but meaningless for security. Always validate form fields on the server — even if you’ve already done it in JavaScript.

if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
    $errors[] = "Invalid email address.";
}

AJAX Submission (Done Right)

AJAX keeps the page from reloading, but needs graceful fallback. Your JS should handle errors and show status messages clearly. Also make sure you're not blocking search engines or assistive tech.

fetch('/api/contact.php', {
    method: 'POST',
    body: new FormData(form)
})
.then(res => res.json())
.then(data => showStatus(data.message));

Spam Keyword Filtering

A simple keyword blocklist catches most of the obvious spam. Add a short delay between submissions to make life harder for bots.

$badwords = ['viagra', 'bitcoin', 'casino'];
foreach ($badwords as $word) {
    if (stripos($message, $word) !== false) exit;
}

Rate Limiting

Track IPs and block rapid repeat submissions. Even a 10-second cooldown per IP address can drastically reduce automated spam.

if (time() - $_SESSION['last_submit'] < 10) {
    exit; // too fast
}
$_SESSION['last_submit'] = time();

Proper Email Headers

Bad headers = instant spam folder. Always set `Reply-To` and `From` fields correctly. Make sure SPF and DKIM are set up for your domain — and avoid sending from user-submitted addresses.

mail($to, $subject, $message, "From: contact@yourdomain.uk\r\nReply-To: ".$clean_email);

Conclusion

Building a decent contact form isn’t hard — it’s just often neglected. The trick is to do the simple things well: validate input, avoid abuse, and make sure messages arrive cleanly and consistently.

Whether you’re building your own or improving an old one, keep it tight, lightweight, and respectful. Forms are the front door of your business — don’t leave it unlocked, and don’t make it a maze.