A Captcha Is Not a Security Control, It Is a Cost Allocation
Who really pays for a challenge, why false positives cluster on the users you can least afford to lose, and what to deploy instead.
Every challenge is a transaction. You spend some of your users' time and patience to buy a reduction in abuse. Whether that trade is good depends entirely on how the cost is distributed — and by default, it is distributed to whoever looks unusual, which is rarely whoever is attacking you.
1. Who pays
A captcha charges everyone who arrives, once per attempt, in seconds and in frustration. The cost falls hardest on people using screen readers, people on slow connections, people with motor impairments, people whose first language is not the one in the image, and people behind a VPN or a shared address that the risk engine already distrusts. Those groups are small in percentage terms and disproportionately represented among your most technically engaged users.
2. Why false positives cluster
Risk scoring is a classifier trained on a distribution that excludes the edges. A user on a corporate VPN, a privacy-focused browser with aggressive fingerprint randomisation, a phone on a carrier network where thousands of subscribers share one address — all of these look statistically odd. If the challenge fires at a fixed threshold, it will fire constantly for exactly those people, and they will experience your site as broken rather than protected.
3. The third-party cost nobody budgets for
Adding reCAPTCHA is not free in privacy terms: it is a request to Google from every visitor, on every page where the widget loads, and it feeds another profile. For a site whose entire pitch is "you can see what is being collected about you", that contradiction is expensive in credibility. It is also a legal question in some jurisdictions, where a challenge that transfers personal data abroad needs a basis you may not have.
4. What to put in front of it
- Rate limits with a real key. Per IP is the easy one and the wrong one alone: per-account, per-session and per-prefix limits catch distributed abuse that IP limits miss and stop punishing shared CGNAT addresses.
- Proof of work. A small JavaScript puzzle costs an attacker real CPU per attempt and costs a legitimate user a few hundred milliseconds. It raises the price of volume without asking a human to prove anything.
- Risk-based challenges instead of blanket ones. Challenge only when the score, the velocity or the target action justifies it, and never on a plain read.
- A second factor that the user already has. For account actions, email confirmation or a passkey is both better security and lower friction than an image puzzle.
- Turnstile or hCaptcha when you need a hosted option — they are non-interactive for most visitors, but validate the token server-side and bind it to the action, or you have added a third-party script for nothing.
5. Measure the trade, not the blocking
Most teams report how much abuse they stopped and never measure what the challenge cost. Instrument it: completion rate per form, time-to-submit, drop-off at the challenge step, and support tickets mentioning verification. A challenge that blocks 90% of automated attempts and loses 8% of legitimate signups is usually a bad deal, and you cannot know that without the second number.
6. The uncomfortable rule
If a page can be read without a challenge, it should be. Put friction on the actions that create value for an attacker — account creation, password reset, checkout, anything that sends a message — and leave discovery alone. Then accept that some abuse will get through: the alternative is paying for it with the users who did nothing wrong.
ipbox.one exists partly to make that trade visible: it shows the signals a risk engine would use about your own connection, which is what "why am I being challenged" usually comes down to.