Tips to avoid spam getting sent from your contact form

Hello everyone. This is just a small tip I discovered the other day and through to share it.

I have recently been receiving some spam emails from the contact form on my web page. Email address are usually something dull like hi@gmail.com or hello@gmail.com with a simple text like: “Hello you are you, I hope you are fine.”

I do not know why these emails exist but I have found a way to avoid them. I have noticed that the bot behind these emails is programmed to fill out every contact entry there is in the form.

I do not collect phone numbers on my contact form so what I did was that I created a new entry method in my contact form called “phone” and set the metadata appropriately, but didn’t set the entry to “required”, then I hid it from view, so the regular user doesn’t see it but the bot does. If you do need to collect phone numbers, maybe you can use another thing that you don’t need, as long as it seems legit (I would think), like a second email or number entry.

Hopes this could help someone. Feel free to let me know how ya’ll deal with these spam emails!

Cheers!

P.S.

I know BSS’s smart forms have built in capcha, but for some reason the bot knows how to pass it. I know this because since I did as described above I haven’t gotten any spam.

I’m not clear on how this stops spam. If the spambot sees the hidden field and fills it out, how does this prevent the submission?

2 Likes

OMG I totally forgot to add the most important part! Here it goes.

You need something like this. (Please do excuse my js, I have only ever learned python).

function isBot(){
    const sendBtn = document.getElementById("sendBtn")
    const telIn = document.getElementById("telIn")
    if(telIn.value){
        console.log("!")
        return;
    } else{
        console.log(":D")
        sendBtn.type = "submit";
        sendBtn.click();
        setTimeout(() => {sendBtn.type = "button";}, 3000);
    }
    
}

If anyone has a better idea how to handle this exact thing (as it can be done in many ways) please let me know.

This code takes the id of the “hidden” input method and makes sure its empty (as the user does not see it and the bot fills it anyway)

In this case I’m using the BSS smartform which uses the send button as “submit”. But just go and change its type to “default” or “button” then the form wont submit until we change it with the js.

Then add “onclick" : “isBot” (our function) to the send button’s attributes so when we click it, it will verify there is nothing in the hidden input field. Then the js makes the button a type=submit and then clicks itself to send the message. Then wait for 3 seconds before returning the button to type=button so the form wont work again. (Until next try)

As a convoluted attempt this might be, it really works, and for months I haven’t gotten those spam emails and neither has my clients. (While still getting messages through the forms of course) But again, if you know better than this please let me know as that’s how I can learn. And I also try to help others here.

Cheers!

P.S.

This is how I make ti disaper:

.tel-in-style {
  position: absolute;
  left: -50000px;
  z-index: -5;
}

I could probably use display none, but I’m unsure on if this also hides it from the bot? This seems to work wonders though. I’d recommend using overflow-x-hidden on its parent just in case…

1 Like

An old trick called ‘honeypot’ …

GPT explained it: Honeypot form fields are a highly effective, invisible, and user-friendly anti-spam technique. By hiding a form field (using CSS/JS) that only automated bots will detect and fill out, submissions containing data in that field can be automatically rejected or flagged, preventing spam without annoying users with CAPTCHAs.

2 Likes

Why this is not effective

1. Client-side checks don’t stop bots

Any bot can:

  • Disable JavaScript
  • Call your backend endpoint directly
  • Remove this function entirely

If your server accepts the form, the bot wins.

Rule: Anything done only in JavaScript is a hint, not a defense.


2. Bots can easily avoid honeypots

Modern bots:

  • Ignore hidden inputs
  • Detect CSS-hidden fields
  • Fill only visible inputs
  • Parse labels and name attributes

So telIn being empty doesn’t prove it’s human.


3. Programmatic clicking is fragile

sendBtn.type = "submit";
sendBtn.click();

Issues:

  • Can double-submit
  • Breaks accessibility
  • Confuses form behavior
  • Can fail on mobile or slow devices

Forms should submit naturally, not via simulated clicks.


4. Returning early doesn’t block submission

If this function is attached to a button click instead of a form submit event, users could:

  • Press Enter
  • Submit via browser autofill
  • Submit via JS injection

What does work (realistic approach)

1. Honeypot — but validated on the server

Client:

<input type="text" name="company" autocomplete="off" tabindex="-1" hidden>

Server:

if (req.body.company) {
    return res.status(400).end();
}

Lightweight
Invisible to users
Not enough alone


2. Time-based check (very effective)

Bots submit instantly.

Server:

if (Date.now() - formLoadedAt < 3000) {
    reject();
}

Stops most bots
No UX impact


3. Rate limiting (critical)

Limit per IP / fingerprint:

  • 5 submissions per minute
  • CAPTCHA only after abuse detected

4. CAPTCHA (last resort)

Use selective CAPTCHA, not always-on:

  • Cloudflare Turnstile
  • hCaptcha
  • reCAPTCHA v3 (score-based)

If you want to keep a honeypot, do it like this

Client (simple and safe)

<form id="contactForm">
  <input type="text" name="company" tabindex="-1" autocomplete="off" hidden>
  <button type="submit">Send</button>
</form>

Server (mandatory)

if (req.body.company !== "") {
    // bot detected
    return res.status(403).end();
}

Bottom line

Your current approach:

  • Easy to bypass
  • Client-only
  • Fragile submission logic

Better approach:

  • Server-side honeypot
  • Time-based validation
  • Rate limiting
  • CAPTCHA only when needed
2 Likes

If you want to give it a try, I made firaform.com. It is a headless form backend service. You build your form anyway you like, then get a URL form Firaform for the form action property.

Inside you can define any field as a honeypot (that is what you just made). If the field is being filled, the submission will be ignored.

Let me know if you tried it. I would definitely love some feedback.