How to stop form spam without a CAPTCHA
Short answer. Score each submission on your server from signals the visitor never sees: a browser check token, a hidden decoy field, time to submit, the email domain and the network. Reject clear bots, send doubtful ones to email confirmation, and accept the rest. This removes the puzzle for real people while keeping a server-side verdict you can log.
Last updated 2026-10-26 ยท Written by the Nolgard team
Steps
- Add an invisible browser check. Load
client.jsand request a token when the form is submitted. It carries no score; the verdict comes from your server.<script src="https://nolgard.com/v1/client.js" data-site-key="SITE_KEY" defer></script> <script> // on submit await Nolgard.ready; const token = await Nolgard.execute({ action: 'signup' }); // send token with the form to your backend </script> - Turn on decoy fields. Nolgard's opt-in honeypot adds one off-screen input with a random name,
aria-hidden="true"andtabindex="-1". Normal autofill does not fill it. A honeypot hit alone is enforced at most aschallenge, neverblock. - Verify on the server and branch on the decision. Use
action: 'contact'for a contact form. Accountless contact-form checks are not metered.const r = await (await fetch('https://nolgard.com/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ secret: process.env.NOLGARD_SECRET, token, remoteIp, action: 'contact' }) })).json(); if (!r.success) return r.error === 'missing-token' ? confirmByEmail() : accept(); // blocked script is not a bot if (r.decision === 'block' || r.decision === 'shadow') return pretendSuccess(); if (r.decision === 'challenge') return confirmByEmail(); return accept(); - Handle blocked scripts. Ad blockers, Brave Shields and Tor at the Safest level can block the script. Verify then returns
missing-token. Treat it as unknown, not as a bot. - On WordPress, use the plugin. There are ready integrations for Contact Form 7, WPForms and WooCommerce.
- Watch first. Run in Observe mode for a week and read the events before you enforce.
Approaches compared
| Approach | Visible to real users? | Stops simple bots? | Stops headless browsers and AI agents? |
|---|---|---|---|
| Visual CAPTCHA (reCAPTCHA v2, hCaptcha) | Yes, when unsure | Yes | Partly; solving services exist |
| Invisible challenge (Turnstile, reCAPTCHA v3) | Rarely | Yes | Partly |
| Honeypot field only | No | Yes | No; agents can skip off-screen inputs |
| Time-to-submit check | No | Yes | No; easy to wait |
| Server-side scoring of browser, network and behaviour (Nolgard) | No visual challenge ever | Yes | Partly; Nolgard will miss some bots, see methodology |
Questions
Is a honeypot enough on its own?
Against form-filling scripts, often. Against browsers driven by an AI agent that reads the visible page, no. Use it as one signal.
Will this hurt accessibility?
Nolgard never shows a puzzle, so there is nothing to solve with a screen reader. The decoy input is hidden from assistive technology with aria-hidden and removed from tab order.
Do contact-form checks count toward my quota?
No. Accountless checks with action contact are not metered.
What should I do with a doubtful submission?
Send it through email confirmation instead of rejecting it. A false positive then costs the visitor one click.
Can I keep my CAPTCHA while I test?
Yes. Run both, compare the two verdicts on your own traffic, then remove whichever you no longer need. See reCAPTCHA migration.