use case · captcha alternatives
The best CAPTCHA alternative is no CAPTCHA.
Updated May 12, 2026
Every "click all the traffic lights" task is a tax on legitimate visitors and a 95th-percentile-still-passes obstacle for modern spam bots. The fix isn't a fancier CAPTCHA — it's moving spam detection off the user's screen entirely. One server-side classification call replaces the friction with a calibrated probability the user never sees.
Why CAPTCHA is the wrong layer
On-page CAPTCHAs solve "is this a real browser session" — a question that stopped being useful around the time bots started running real headless browsers. They don't solve "is this content spammy," which is the question that actually matters. A bot driving Chromium can pass reCAPTCHA v3 reliably; a real customer trying to fill out a contact form on their phone often can't.
The cost shows up in conversion data. Form-completion rates typically drop 3-15% when reCAPTCHA is on the page, and the drop is concentrated in users on flaky connections, older browsers, and accessibility tools — exactly the visitors who shouldn't be paying the tax. Meanwhile the spam keeps coming, because spam is a content-classification problem and CAPTCHA is a session problem.
The replacement pattern
The form goes from this:
<!-- before: reCAPTCHA on every form, every visitor pays the friction tax -->
<form action="/api/contact" method="POST">
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<div class="g-recaptcha" data-sitekey="6Lc..."></div>
<button type="submit">Send</button>
</form>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>To this:
<!-- after: clean form, classification on the server -->
<form action="/api/contact" method="POST">
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>And the work moves to the server-side handler that was already processing the form anyway. About 15 added lines:
// /api/contact handler — what reCAPTCHA was protecting, now done server-side.
const SPAM_THRESHOLD = 0.85;
export async function POST(req: Request) {
const { email, message } = await req.json();
let probability = 0;
try {
const resp = await fetch("https://api.siftfy.io/v1/predict", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.SIFTFY_KEY!,
},
body: JSON.stringify({ text: message }),
signal: AbortSignal.timeout(2000),
});
if (resp.ok) ({ spam_probability: probability } = await resp.json());
} catch {
// Fall open on transport failure — losing one borderline lead beats
// breaking the form for everyone during a Siftfy outage.
}
if (probability >= SPAM_THRESHOLD) {
// Always 200 to the form; the user sees "thanks" and the spammer
// doesn't learn the threshold.
return Response.json({ ok: true });
}
await sendEmail({ to: process.env.INBOX!, from: email, body: message });
return Response.json({ ok: true });
}The user's experience is the form they expected. The score never touches the browser, so spammers can't probe for it. And the third-party Google script that was tracking every visitor is gone — privacy improves and your CSP gets simpler.
What about hCaptcha, Cloudflare Turnstile, friendly-captcha?
They solve the privacy and accessibility problems with reCAPTCHA but they don't solve the layer problem. They're still asking "is this a real browser session" — they're just asking it more politely. A motivated bot using a real browser still passes; a real customer on a stale browser still occasionally fails. They're improvements, not a rethink.
The case for keeping a Turnstile-style invisible challenge is when you face volumetric abuse — someone is firing 1,000 submissions a second to exhaust your free tier. A stop-the-flood layer makes sense there. But for content quality — junk vs. real — the right tool is content classification.
Edge cases worth handling
- Volumetric abuse. Pair Siftfy with a basic rate-limiter (per-IP or per-session). Classification is per-message; it doesn't help when a script is brute-forcing the endpoint. One IP, 100 requests/min, all blocked at the edge — that's a different layer.
- Honeypot + Siftfy. A hidden form field that real users won't fill in is a free extra signal that costs zero user friction. Reject anything where the honeypot is non-empty before calling Siftfy — saves a request quota.
- Multilingual forms. The model is primarily English-trained. Raise the block threshold to 0.92 if you serve non-English markets.
- Don't reveal the score. Always 200 on hard block. A 4xx tells the spammer to rephrase; a 200 tells them their content shipped, and they stop tuning.
10,000 submissions / month free, no card. Read the /v1/predict reference, or how honeypots fit alongside classification. Related use cases: contact forms, comments, signups.