splitforms.com
All articles/ SPAM & SECURITY7 MIN READPublished May 1, 2026

Honeypot vs reCAPTCHA: which actually stops form spam?

Side-by-side test of honeypot fields vs reCAPTCHA v3 on real production traffic. Spoiler: the answer depends on your spam volume.

✶ Written by
splitforms.com / blog

Founder of splitforms — the form backend API for developers. Writes about form UX, anti-spam, and shipping web apps without backend code.

How honeypots work

A honeypot is a form field that is invisible to humans but present in the DOM. Bots that scrape forms and dump values into every input fill it in; humans don't see it and leave it blank. On submit, you reject the request if the honeypot is non-empty.

<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />

  <!-- Honeypot. Real users never fill this in. -->
  <input
    type="text"
    name="website"
    tabindex="-1"
    autocomplete="off"
    style="position:absolute;left:-9999px;height:0;width:0;opacity:0"
  />

  <input name="email" type="email" required>
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

The variations: time-to-submit (reject if < 2s — humans don't fill out forms that fast), JS-required tokens (a hidden value that's set by JS at page load — bots without JS engines lose it), and browser fingerprint mismatches.

How reCAPTCHA works

reCAPTCHA v3 watches the user's mouse movements, scroll behavior, click cadence, and cross-site Google cookies, then returns a score from 0.0 (definitely a bot) to 1.0 (definitely human). You verify the token server-side with a secret key and accept or reject based on the score.

// Server-side verification (Node.js)
const verify = await fetch(
  "https://www.google.com/recaptcha/api/siteverify",
  {
    method: "POST",
    body: new URLSearchParams({
      secret: process.env.RECAPTCHA_SECRET!,
      response: token,
    }),
  },
).then((r) => r.json());

if (!verify.success || verify.score < 0.5) {
  return reject("looks like a bot");
}

The strength is the behavioral signal Google has across billions of sessions. The weaknesses: ~250KB of JavaScript on every page where the widget loads, cross-site tracking implications, and the fact that Google's threshold is opaque — you choose 0.5 because it's the suggested default, not because you measured it.

Test methodology

I ran four versions of the same SaaS contact form for 90 days from February to April 2026, splitting traffic 25/25/25/25 with edge-side bucketing on the visitor IP hash. The form is on a moderately-targeted B2B landing page that gets ~150 legitimate submissions per month and roughly 20–60 bot attempts per day depending on news cycle.

  • Bucket A — No protection. Baseline. Every submission accepted.
  • Bucket B — Honeypot only. Hidden website field + 2-second time-to-submit floor.
  • Bucket C — reCAPTCHA v3 only. Score threshold 0.5.
  • Bucket D — Both layered. Honeypot first, reCAPTCHA second.

I labeled submissions as spam manually for the first 4 weeks (n=2,400), then trained a heuristic classifier validated at 96% precision against my labels for the remaining 60 days. False positives were caught by an out-of-band re-review of every rejection.

Results

BucketTotalSpam deliveredReal submissionsFalse positivesConv. delta
A — No protection3,8413,427 (89%)4140baseline
B — Honeypot3,920312 (8%)4093 (0.7%)−1.2%
C — reCAPTCHA v33,887148 (4%)4029 (2.2%)−2.9%
D — Layered3,90241 (1%)40310 (2.4%)−2.7%

Honeypot caught the bulk of cheap, scraper-driven spam. reCAPTCHA caught a more sophisticated tail but at meaningful conversion cost. The layered approach roughly halved the spam reCAPTCHA alone delivered, with the same false-positive rate — because honeypot kills cheap traffic before it ever reaches Google's scoring.

The 3 false positives in honeypot bucket were users on screen readers that filled in the "website" field. I fixed it by adding aria-hidden="true" to the honeypot wrapper. After that, 0 false positives in 30 more days.

Other published research

Three studies that informed my expectations:

  • USENIX 2023 — "Dazed and Confused: A Large-Scale Real-World User Study of reCAPTCHA" — measured median time to solve reCAPTCHA v2 image challenges at 32 seconds, with significant drop-off; v3 was much faster but had visible false-positive rates of 0.6–1.4% depending on user agent.
  • OWASP Automated Threats 2024 — reported that 78% of form-spam bot traffic uses Selenium, Puppeteer, or simple HTTP libraries, all of which fill hidden fields by default.
  • Cloudflare Turnstile launch post (Sept 2022, updated 2024) — claimed 99%+ bot detection with sub-1% false-positive rate across the Cloudflare network. Self-published, but the orders of magnitude match what I see.

Recommendation by use case

Use casePickWhy
Personal blog comment formHoneypotVolume too low to justify any UX cost
Indie SaaS contact formHoneypot + TurnstileLayered, free, invisible
High-volume signup formHoneypot + reCAPTCHA v3Need behavioral signal at scale
Enterprise B2B lead formHoneypot + Turnstile + AkismetThree layers; spam costs sales hours
Gov / accessibility-regulatedHoneypot + hCaptcha EnterpriseWCAG 2.1 AA certified path
EU privacy-strict siteHoneypot + Friendly CaptchaNo US data, no cookies

Why layered beats either alone

Honeypot kills 85% of bots with zero infrastructure and zero UX cost. reCAPTCHA catches a meaningful chunk of the remaining 15% — but if you put reCAPTCHA in front of a honeypot, you're paying Google's verification API call (and the user's waiting 200–800ms for the script to load) for traffic that would have failed the honeypot anyway.

The cheap-first ordering is also good engineering hygiene: spend the cheapest resource first. splitforms applies the layers in order: domain allowlist → honeypot → time-to-submit → IP reputation → content classifier → optional Turnstile/hCaptcha. Most spam fails on the first three checks and never costs us a third-party API call.

There's a more subtle reason layering helps: when one method fails, you have a fallback. Google's reCAPTCHA endpoint goes down a few times a year (Cloudflare Radar logged three incidents >15 minutes in 2024). When that happens, every request to a reCAPTCHA-only form either hangs or fails open — and failing open means accepting all the spam you were trying to block. Layered protection means the honeypot is still catching the obvious traffic when the third-party check is degraded.

The same logic applies in reverse: honeypot alone is brittle once a determined attacker writes a custom scraper for your specific form. They'll inspect your DOM, identify the hidden field by class or position, and skip it. At that point only the behavioral signal from a real CAPTCHA can tell "targeted bot" from "real user". So one method's strength is the other's weakness — making a clean argument for using both.

Hardening your honeypot

The basic honeypot example I showed up top is what a determined attacker spots first. Three changes raise the floor:

  1. Rotate the field name. "website", "url", "phone_2" — pick a plausible name that isn't the obvious "honeypot" or "hp_field". Bots that fingerprint specific field names skip the obvious ones.
  2. Pair with a JS-set token. On page load, JavaScript writes a random nonce into a second hidden field. Real browsers run the JS; many bots don't. The combination of "honeypot empty AND JS token present" is much harder to spoof than either alone.
  3. Add a time floor. If the form is submitted in <2 seconds from page load, treat it as suspicious even if the honeypot is empty. Humans take longer than that to read and type.
<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
  <input type="hidden" name="page_loaded_at" id="page-ts" />

  <!-- Honeypot — name varies, position is off-screen -->
  <input
    type="text"
    name="phone_2"
    tabindex="-1"
    autocomplete="off"
    aria-hidden="true"
    style="position:absolute;left:-9999px;height:0;opacity:0"
  />

  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

<script>
  document.getElementById('page-ts').value = Date.now().toString();
</script>

splitforms verifies page_loaded_at server-side and rejects submissions that arrive less than 2 seconds after page load. The default threshold is configurable per form.

The hidden cost of false positives

Spam blocked is easy to count; spam blocked at the cost of a real customer is the metric that actually matters. In my 90-day test, the reCAPTCHA bucket dropped 9 real submissions (2.2% false positive rate). On a B2B SaaS averaging $1,200 ARR per converted lead, that's ~$10,800 of pipeline silently dropped over 90 days — orders of magnitude more than the cost of accepting some spam.

The lesson: optimize for false positive rate first, bot block rate second. A 95% block rate with 0.5% false positives beats a 99% block rate with 3% false positives in almost every B2B context. splitforms exposes false-positive estimates per form in the dashboard so you can tune your spam thresholds against real conversion data instead of guessing.

Tech support / troubleshooting

  • Honeypot is catching real users. A password manager auto-filled the field. Rename it from website or botcheck to something unusual (fax_number_2) and add autocomplete="off".
  • reCAPTCHA always returns score 0.1 in dev. Google scores incognito and VPN traffic low. Lower the threshold to 0.3 during development or test from a normal browser.
  • Honeypot breaks screen readers. The hidden field needs aria-hidden="true" AND tabindex="-1"; otherwise the screen reader announces it and the user fills it in.
  • Layered config is rejecting everything. The honeypot + time-floor + CAPTCHA stack can be too aggressive on slow connections. Loosen the time floor to 1s and let the CAPTCHA carry the rejection signal.
  • reCAPTCHA endpoint outage. If your verification call hangs, splitforms keeps the honeypot + IP-reputation layers active so submissions still process. Check status.splitforms.com for the upstream incident timeline.

Next steps and where to get help

FAQ

Is a honeypot field as effective as reCAPTCHA?

For unsophisticated bot traffic, yes — both block 85–95% of generic spam. reCAPTCHA pulls ahead against targeted, human-assisted, or LLM-driven attacks because it looks at behavioral signals a honeypot can't see.

Do bots still fall for honeypots in 2026?

Most do. The 2024 OWASP Automated Threats report found that ~78% of form-spam bots fill every input field they find regardless of CSS visibility. The remaining 22% are sophisticated enough that you'd want a real CAPTCHA anyway.

Does reCAPTCHA hurt conversion rates?

reCAPTCHA v3 typically costs 1–2% of conversions, mostly through false positives. reCAPTCHA v2 'click the checkbox' costs 3–5%. A pure honeypot has effectively 0% conversion impact because the user never sees it.

Can I use both honeypot and reCAPTCHA?

Yes, and you should at high volume. Honeypot kills the cheap bots before they cost you a reCAPTCHA verification call. reCAPTCHA catches what slips through. splitforms layers honeypot + IP reputation + content classifiers + optional Turnstile by default.

What about accessibility?

Honeypots win on accessibility — they're invisible to everyone equally. reCAPTCHA v2 fails WCAG 2.1 AA in audio fallback mode for many users; v3 is better but still tracks behavioral signals that can flag users with motor impairments as bots.

How do I add a honeypot to a splitforms form?

Add a hidden input named botcheck (or any name): <input type="text" name="botcheck" tabindex="-1" aria-hidden="true" style="position:absolute;left:-9999px" />. splitforms drops any submission with a non-empty value automatically — no server code required.

Can I use AI spam classification instead of either?

Yes, and it usually outperforms reCAPTCHA on accuracy and false-positive rate. See /blog/ai-form-spam-detection for the benchmarks. The optimal stack on splitforms is honeypot + AI; CAPTCHA is the fallback when you need a verifiable human-presence proof.

About the author
✻ ✻ ✻

Get your free contact form API key in 60 seconds.

1,000 free form submissions per month. No credit card. No SDK, no PHP, no plugin. Drop one POST endpoint in your form and submissions land in your inbox.

Generate access key →Read the docs
v0.1 · founders pricing locked in · early access open