Back to Engineering Blog
DevOps6 min read

Replacing a Fake Checkbox with Real Bot Protection

The old verification control was a div with an 800ms spinner that blocked nothing. Moving to reCAPTCHA v3 scoring, and rate limiting that fails closed.

Glacro · Published July 22, 2026

Our sign-up and contact forms displayed a verification control. It was a div with a checkbox drawn in CSS and an 800ms spinner, followed by a tick. It verified nothing whatsoever.

Why it was worse than nothing

A bot does not render your page. It posts to the endpoint directly, and the endpoint never asked for proof of anything. The control existed only for humans, who were the ones already behaving. Meanwhile it created a confident impression internally that bot protection was handled.

reCAPTCHA v3, scored server-side

The forms now obtain a token from reCAPTCHA v3 and the server verifies it with Google, checking both the score and that the action matches the form that claims to have produced it. Below the threshold, the request is rejected. There is no puzzle for the user — v3 scores silently.

typescript
const result = await requireHuman(token, { expectedAction: "signup" });
if (!result.ok) {
  return NextResponse.json({ error: result.error }, { status: result.status });
}
Key Takeaway
If the secret key is not configured, the check reports that it is not running and logs it on every request, rather than pretending to pass. If Google is unreachable, the request is refused rather than waved through.

Rate limiting underneath

Scoring is not a substitute for a limit. Auth and admin endpoints carry their own per-identity rate limits, so a credential-stuffing run is bounded regardless of how it scores.

Related Articles

Product Updates

What Glacro Actually Runs, and What It Doesn't

August 26, 2026 · 6 min read
Tutorials

Deploying a Next.js 14 Static Export

August 20, 2026 · 7 min read