Spam Prevention

Honeypot vs. reCAPTCHA: Which Spam Defence Belongs on a Static-Site Form?

Honeypots cost nothing and catch most bots. reCAPTCHA catches more and costs your visitors something real. Here is how to decide which layers your form actually needs.

· SimpleForm Team

Every form that has been live for a week has a spam problem. The question is never whether to defend it, only which defences are worth what they cost — and the costs are not all paid by you.

There are really three tools in common use for static-site forms: honeypot fields, rate limiting, and a challenge service like reCAPTCHA. They are not alternatives. They catch different things, fail in different ways, and belong in a specific order.

What is a honeypot actually catching?

A honeypot is a form field that a human never sees and a bot cannot resist. You add an input, hide it from visual and assistive presentation, and reject any submission that arrives with it filled in.

It works because of how form-spam bots are built. The economical way to spam forms at scale is to fetch a page, find every input element, put something plausible in each one, and POST. There is no rendering, no CSS evaluation, and no judgement about which fields a person would have seen. A field named something like _honeypot gets filled exactly like the message box does.

What makes this the first layer worth adding is the cost profile. It is a few lines of HTML. It adds no third-party script, no network round trip, no cookie, and no visible friction. Legitimate visitors never know it is there. On SimpleForm any field whose name begins with an underscore is treated as a trap automatically, so the entire implementation is one hidden input.

Implemented carelessly, though, it can cause harm. Three details matter:

  • Hide it off-screen, not with display:none. Absolute positioning far to the left keeps it out of the visual flow while behaving more predictably across assistive technology.
  • Take it out of the tab order and turn off autofill. A tabindex of -1 and autocomplete="off" stop keyboard users and password managers from stumbling into it.
  • Mark it aria-hidden. A screen reader that announces the trap field will produce exactly the false positive you were trying to avoid.

Why does a honeypot stop working?

Because it is a trick, and tricks are learnable. A bot written specifically for your site — rather than for forms in general — will be told which field to skip. The moment your form is worth targeting individually, the honeypot stops being the whole answer.

In practice, that threshold is higher than most people assume. The overwhelming majority of form spam is broadcast, not targeted: the same script hitting millions of forms, optimising for volume rather than for any particular victim. A honeypot removes essentially all of it.

The forms that outgrow honeypots have something specific worth attacking. Anything that results in an outbound email to an address the submitter controls, anything that gets read by a human who might click a link, anything attached to a valuable brand. Job application forms and quote request forms attract more targeted attention than a personal blog's contact page ever will.

What does rate limiting add?

A ceiling, and it is the layer people most often skip.

Honeypots and challenges both try to distinguish a bot from a human on a single submission. Rate limiting does not care about that question at all — it just observes that one source is submitting far more often than any person would. That makes it complementary rather than redundant: it catches the targeted attacker who defeated your honeypot, and it caps the damage of anything else that gets through.

Two design choices decide whether it helps or hurts:

  • Scope it per endpoint, not per site. A shared counter means a flood against one form degrades every other form you run.
  • Return a clear status. A limited request should return 429, not a generic error and not a silent success. Silent success is worse than either, because it teaches a spammer nothing while teaching you nothing.

The realistic false-positive scenario is a large shared network — a school, a big office, a mobile carrier's NAT pool — where many genuine people appear as one address. If that describes your audience, raise the threshold. Do not remove the layer.

When is reCAPTCHA worth its cost?

reCAPTCHA and similar challenge services are the most effective single defence available, and they are the only one with a real price tag attached — just not a monetary one.

What you pay:

  • Third-party script on every page with a form. That is a render-blocking dependency you do not control, on a page you were probably keeping static specifically to avoid dependencies.
  • Visitor data leaving your site. The scoring works by observing behaviour, which means information about your visitors goes to a third party. That has consequences for your privacy notice and, in some jurisdictions, for consent.
  • Occasional false positives. Score-based versions run invisibly most of the time, but privacy-conscious visitors — the ones running strict browser settings, VPNs, or content blockers — score worse simply for being careful. Those are frequently the exact people a developer-tools business most wants to hear from.

Against that, it is genuinely effective, including against targeted attacks that a honeypot cannot see. The honest framing is that it is a heavy tool for a heavy problem. Reach for it when spam has actually become a problem you can measure, not pre-emptively because it feels like the responsible thing.

How should you layer these in practice?

The ordering that costs the least and catches the most:

  • Always on: honeypot. Free, invisible, catches the broadcast majority.
  • Always on: rate limiting. Free, invisible, caps everything else.
  • Always on: origin restriction. If your form only ever posts from your own domain, say so. It costs nothing and eliminates the laziest form of abuse, which is someone POSTing straight at your endpoint from a script.
  • On demand: a challenge service. Enable it on the specific forms under pressure, once you have evidence of pressure.

The pattern to avoid is enabling everything on day one. Every layer you add is something that can produce a false negative you never learn about — because a rejected legitimate submission does not send you an email complaining. The person just leaves.

How do you know whether it is working?

Count what you block, not just what you receive.

A defence you cannot measure is a defence you cannot tune. If your form backend records the reason a submission was rejected, you can see the shape of your spam: mostly honeypot hits means broadcast bots and your current setup is fine; mostly rate-limit hits means something is targeting you and a challenge layer may now be justified; a rising count of nothing at all, with junk still arriving in your inbox, means the traffic is human and no automated layer will help.

That last case is worth naming, because it is the one people misdiagnose most. A steady trickle of low-quality vendor pitches through your contact form is not a spam problem in the technical sense. No honeypot catches a real person typing a real message. That is a triage problem, and the fix lives in how you route and filter what arrives — not in another layer in front of the form.

Frequently asked questions

For a low-profile contact form on a small site, usually yes. Honeypots stop the indiscriminate form-filling bots that generate the overwhelming majority of junk submissions. They do not stop a human or a scripted attack written specifically against your form, which is why rate limiting belongs underneath them.

Because it is not free from the visitor perspective. It loads third-party script on every page carrying the form, sends data about your visitors to a third party, and occasionally challenges legitimate people. Those are real costs that should be paid only where the spam pressure justifies them.

They can, if implemented carelessly. A field hidden with display:none may still be announced by some assistive technology, and password managers may try to fill it. Position it off-screen, set tabindex to -1, disable autocomplete, and mark it aria-hidden.

Rarely, if the limit is set sensibly and applied per endpoint rather than site-wide. The realistic false-positive case is a large office or school behind a single shared IP address. If your audience is likely to share addresses at scale, raise the limit rather than removing the layer.

Ship a working form in five minutes. Point your form's action at a SimpleForm endpoint and submissions land in your inbox and dashboard straight away. Start free or read the docs.

More from the blog