Skip to main content

Protecting Your Website from Spam and Bots

Security · 15.03.2025 · 5 min read
Illustration for “Protecting Your Website from Spam and Bots”

Protection against spam bots

Spam does not come from people — it comes from scripts that fill out thousands of forms across the internet every second. Polite requests and captchas that are never checked on the server will not stop them, only concrete technical barriers that you control yourself will.

Google reCAPTCHA

The most common approach. reCAPTCHA v2 shows the user an "I'm not a robot" checkbox or a set of images, v3 works invisibly and assigns every request a number from 0 to 1 — the probability that a human is behind it. You set the cutoff threshold yourself, usually 0.5.

Verification on the server

The widget on the page is only half the protection. The token it sends has to be passed to your server and checked with a request to the Google API, otherwise any bot that copies someone else's valid token from the page source will pass the form.

curl -X POST https://www.google.com/recaptcha/api/siteverify \
  -d secret=YOUR_SECRET_KEY \
  -d response=TOKEN_FROM_FORM

# Response with a wrong secret key:
{"success": false, "error-codes": ["invalid-input-secret"]}

# Response with an expired or reused token:
{"success": false, "error-codes": ["timeout-or-duplicate"]}

invalid-input-secret means the secret key in the request does not match the site key in the Google console — people often mix up the test key and the production key. timeout-or-duplicate means the token was already used or expired (it lives about two minutes), which is normal when a form is resubmitted after a validation error.

By connecting reCAPTCHA you send visitor data (IP address, on-page behaviour) to Google — this has to be stated in your site's privacy policy, otherwise you violate the requirement to disclose third-party data processing. Cloudflare Turnstile sends less data and often works without any visual check at all, which makes it a more privacy-friendly alternative for sites aimed at the European Union.

Honeypot field

Add a hidden field to the form — bots fill it in, humans do not:

<input type="text" name="website" style="position:absolute;left:-9999px" tabindex="-1" autocomplete="off" aria-hidden="true">

On the server you simply check: if the website field is not empty, it is a bot — drop the request without an error message (the bot does not need to know it was caught).

Common configuration mistakes

Checking only in the browser

If JavaScript code merely hides the submit button until the captcha passes, and the server never checks the token, that protects against honest users, not against bots. A bot sends the POST request directly, bypassing the browser and your entire JS check.

display:none instead of moving off-screen

<!-- Some browser autofillers ignore display:none and still fill in
the user's saved email address anyway -->
<input type="text" name="email2" style="display:none">

<!-- Moving the field off-screen is more reliable for the same purpose -->
<input type="text" name="email2" style="position:absolute;left:-9999px">

Comparing protection methods

MethodNeeds JavaScriptVisible to the userSends data to a third party
reCAPTCHA v2YesYes, checkbox or imagesYes, Google
reCAPTCHA v3YesNoYes, Google
Cloudflare TurnstileYesUsually noYes, Cloudflare
Honeypot fieldNoNoNo

A honeypot sends nothing outside and needs no consent for third-party data processing, but it only stops primitive bots that fill in every field in a row. Against targeted spam on a specific site it is combined with reCAPTCHA or Turnstile.

When anti-spam measures will not help

  • Manual spam left by real people for a small fee — no captcha tells such a person apart from a regular visitor.
  • An attack through the site's API that bypasses the HTML form: if the endpoint accepts the same fields directly, the honeypot and the captcha widget on the page are useless — the check needs to live on the API itself.
  • An outdated anti-spam plugin with a known bypass vulnerability — regular updates are mandatory, a captcha by itself does not patch holes in code.

One more honest point: any visible captcha lowers form conversion — some real visitors leave without solving the images or without waiting for the widget to load on a slow connection. Invisible reCAPTCHA v3 and a honeypot barely create this problem precisely because they require no action from a human.

For WordPress

  • Akismet — comment protection, trained on data from millions of sites
  • WPForms with reCAPTCHA — protection for contact forms
  • Cloudflare — protection at the DNS/CDN level before the request ever reaches WordPress

Checking that the protection works

  1. Submit the form without filling in the honeypot field — it should go through as usual.
  2. Fill in the honeypot manually through the browser developer console and submit the form — it should be rejected.
  3. Check the server log for invalid-input-secret and timeout-or-duplicate responses from Google — their absence does not prove the check works, make sure the code really calls siteverify rather than just checking whether a token is present.
  4. After a week, compare the number of submissions in the database with the number of form completions in analytics — a sharp gap means a bot is still getting through.

For more on protecting WordPress from a break-in, see hardening WordPress, and for filtering malicious traffic at the web server level, see ModSecurity WAF.

Was this article helpful?
← Back to Knowledge Base Ask Support