A working CAPTCHA does two things at once: it blocks bots, and it taxes every real reader. The blog comment is a low-stakes interaction — your reader has already read the post, they're choosing to engage, and you've just asked them to identify traffic lights. The drop-off is bigger than people realize. Published numbers from Stanford, Google, and various A/B tests put visible reCAPTCHA v2 drop-off at 5–15% of attempted submissions, and invisible variants at 1–4%.
If you only have ten comments a week, that drop-off is theoretical. If you have a thousand, you're losing fifty real comments to friction every week. The good news: every countermeasure that CAPTCHA targets has a less visible equivalent.
Layer 1: the honeypot field
A honeypot is a form field hidden from humans (typically with position: absolute; left: -9999px and aria-hidden="true") but visible to most bots that scrape and fill every input. When the field comes back populated, you know the submission is automated.
Honeypots are not a complete defense — sophisticated bots render the page in a headless browser and skip hidden fields. But they catch 60–80% of low-effort spam for almost no implementation cost, and they cost real readers nothing. The one trap to avoid: don't label the field anything a password manager will autofill ("address", "phone"). Name it something implausible like comment_url when your form doesn't actually need a URL.
More detail in the honeypot anti-spam post, including the accessibility traps.
Layer 2: server-side text classification
This is where the heavy lifting happens. The submitted comment goes to a classifier, the classifier returns a spam probability, and your code decides what to do with it. The three-bucket pattern works well for comments: drop above 0.85, queue for review between 0.5 and 0.85, publish below.
The visible UX is the same as before the change: reader hits submit, sees "comment posted" (or "comment held for review"). Behind the scenes, the spam decision has already happened. No puzzle. No image grid. No "I'm not a robot" checkbox.
Layer 3: time-trap fields
Record the time the form renders and the time it submits. Human readers take at least a few seconds to read a post and form a comment; bots typically submit in under 500ms. Reject submissions that arrive faster than humans plausibly type. The threshold is low-effort — 2 seconds is generous and false positives are rare.
Layer 4: rate limiting
Cap submissions per IP, per session, and per email domain over a rolling window. A single visitor posting 30 comments in a minute is either a bot or someone you don't want anyway. This is the cheapest layer to add and the easiest to forget — wire it before any of the above.
The fallback layer: keep CAPTCHA wired but off
Real abuse campaigns happen. Someone with a botnet decides your blog is target practice for a weekend. The right response isn't permanent CAPTCHA — it's the ability to turn one on for 24–72 hours and back off when the wave passes. Keep a configured implementation (we recommend hCaptcha or Cloudflare Turnstile, both with reasonable free tiers and less abusive UX than reCAPTCHA), gated behind a config flag.
Why this layered setup wins
Each layer catches something the others miss, and only the fallback is ever visible to a real reader. Naïve bots get stopped by the honeypot; medium-sophistication ones get stopped by the classifier; the long tail (timing, IP patterns) gets stopped by rate limiting. The everyday cost to real readers is zero.
Specifically: an established blog running this exact setup (honeypot + Siftfy classifier + 5-comment-per-minute rate limit) is the integration shape we point most new customers at. No CAPTCHA, no measurable real-reader friction, and the spam queue stays empty enough to review in five minutes a day.