Pot of honey beside calculator

3 Tricks to Help Prevent SPAM in Your Contact Forms

TLDR;

If your contact form is getting flooded with spam, here are 3 DIY techniques which can help to keep it under control:

  1. Use a basic math captcha to stop the simplest bots
  2. Add a hidden honeypot field that real users won't see but bots might fill out
  3. Check if JavaScript is enabled (makes it a bit more likely the page is being viewed in an actual web browser)

Note that these aren't foolproof, especially in the age of AI and browser based agents. You could also consider tracking heuristics like the time it takes to fill out the form and blocking suspicious submissions (e.g. if it's filled out too quickly). If you need something more robust than a DIY approach, Google's reCAPTCHA is a solid choice since it uses machine learning to continually improve its accuracy.

1. Add a Simple Math Captcha

One of the easiest anti-spam measures is a simple math captcha. No need for weird squiggly letters or third-party services. We just generate a basic math question like "3 plus 7" and ask the user to solve it before submitting.

Here’s how we generate the question server-side in our controller:

PHP
// ContactController.php

public function show()
{
    $num1 = rand(1, 10);
    $num2 = rand(1, 20);
    session(['captcha_question' => "$num1 plus $num2", 'captcha_answer' => $num1 + $num2]);

    return view('contact');
}

And in the Blade view, we show the question and expect an answer:

BLADE
<label for="captcha">Captcha</label>
<input type="text" name="captcha" id="captcha" required>
<p>Please solve: {{ session('captcha_question') }}</p>

Then in the form request, we compare the user’s answer with the one stored in the session:

PHP
'captcha' => ['required', 'numeric', function ($attribute, $value, $fail) {
    if ($value != session('captcha_answer')) {
        $fail('The captcha answer is incorrect.');
    }
}],

Finally in order for the user to see an error if they're a little bit rusty with their math, or if there are any other form errors, we can add a simple loop to the top of the page:

BLADE
@if($errors->any())
    <div class="mb-6 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
        <ul class="list-disc pl-5">
            @foreach($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
Pro Tip You can tweak the difficulty by using larger numbers or even subtraction/multiplication. Just keep it simple enough for humans to solve quickly.

2. Add a Honeypot Field

A honeypot field is a hidden input field that normal users never see, but bots sometimes like to blindly fill out. If this field contains any data, we know it’s a bot submission and we can safely ignore it.

Here’s the honeypot field added to our Blade form:

BLADE
<div class="hidden">
    <label for="honeypot">Leave this empty</label>
    <input type="text" name="honeypot" id="honeypot">
</div>

And in our validation rules, we simply check that it remains empty:

PHP
'honeypot' => ['nullable', function ($attribute, $value, $fail) {
    if (!empty($value)) {
        $fail('The form submission is invalid.');
    }
}],
Pro Tip You could even use less conspicuous names like `address` or `phone_number` instead of honeypot (assuming these don't actually appear on your form as valid fields). Bots are more likely to fill those in automatically.

3. Use a JavaScript-Based Checksum

This one’s a bit sneakier. Some bots don’t load or execute JavaScript. We can add a hidden field with a default value (like 1) that gets changed to 2 with JavaScript on page load. If the value is wrong, we reject the form.

Here’s the hidden input:

BLADE
<div class="hidden">
    <label for="checksum">Checksum</label>
    <input type="text" name="checksum" id="checksum" value="1">
</div>

Then we update it with JS when the DOM loads:

JS
@section('scripts')
<script>
    document.addEventListener('DOMContentLoaded', function() {
        // Change the checksum value to 2
        document.getElementById('checksum').value = 2;
    });
</script>
@endsection

In the validation rules, we check the value:

PHP
'checksum' => ['required', 'numeric', function ($attribute, $value, $fail) {
    if ($value != 2) {
        $fail('Something went wrong while trying to submit the form.');
    }
}],
Pro Tip You can get creative with this. For example, detecting certain DOM events like mouse movement to particular areas of the page (e.g. the submit button). Most bots won’t bother figuring it out... well some of the older ones at least.

Wrap-Up

All three techniques are easy to add and don’t require external services or JavaScript frameworks. They work well together and catch different types of bot behaviour. None of them are perfect on their own, but combined they make it a bit harder for automated spam to get through. And if you need more advanced protection beyond the DIY approach, there's always tools like Google reCAPTCHA that can help as well.

Up Next

How to Add Dark Mode to Your Laravel Website (SSR) Using Tailwind 4.x . ' Featured Image'

How to Add Dark Mode to Your Laravel Website (SSR) Using Tailwind 4.x

In this post we'll see how simple it is to add a dark mode toggle to your Laravel website using Tailwind 4.x. Although there are a few different ways to accomplish this, this post focuses on...

August 13, 2025 • 3 min read
12 Simple Tips for Securing Your Web Apps . ' Featured Image'

12 Simple Tips for Securing Your Web Apps

In this post I share some simple tips you can use to improve the security of your web applications. Although many of these are best practices which apply to cloud-hosted web apps in general, I'll also share a few Laravel-specific code samples to...

August 1, 2025 • 7 min read