reCAPTCHA Is Still Spying on Your Users — Here’s What to Do About It
Let’s be honest: reCAPTCHA was never about you. It was about training Google’s image recognition models on your users’ dime, wrapped in a thin veneer of “security.” Every “select all squares with a bus” challenge sent behavioral telemetry straight to Mountain View. And now that AI scrapers are hammering every self-hosted service in existence, the spam problem has gotten worse while the privacy trade-off hasn’t budged.
Here’s the thing — if you’re already running Gitea, Nextcloud, Forgejo, or any other self-hosted service, you probably care about data sovereignty. Bolting reCAPTCHA onto the front door kind of defeats the point.
The good news: there are actual alternatives now. Some are self-hosted, some are third-party but privacy-respecting, and at least one is explicitly designed to bodycheck AI crawlers before they even touch your app. Let’s walk through what’s worth your time in 2026.
The Landscape: What Even Counts as a CAPTCHA Alternative?
Before diving in, it’s worth clarifying what we’re actually talking about. “CAPTCHA alternative” covers a few distinct approaches:
- Proof-of-Work (PoW): Make the client’s browser do computational work. Bots can do it too, but it costs them time and resources. Scale the difficulty and you price them out.
- Behavioral/ML detection: Analyze mouse movements, timing, browser fingerprints. reCAPTCHA v3’s approach — and Cloudflare Turnstile’s. Works well but requires a third-party service.
- Anonymous tokens: A newer cryptographic model (Privacy Pass) where users prove they’re human once and spend tokens later — no tracking, no challenges.
- Reverse proxy gating: Sit a challenge layer in front of your entire service. Anubis does this. It’s blunt, it’s effective, and it’s become extremely popular for a reason.
The Contenders
Cloudflare Turnstile — External, But Actually Fine
Turnstile isn’t self-hosted. Stop, don’t click away — hear me out.
Cloudflare’s pitch: no annoying puzzles, no data sold to advertisers, ML-based challenge that’s invisible to most legitimate users. It’s free up to 1 million requests/month. Compared to reCAPTCHA, it’s dramatically less invasive — Cloudflare at least has a privacy policy that doesn’t make lawyers wince.
If you’re already using Cloudflare Tunnels or Pages, Turnstile is the lowest-friction path. Add it to a form in about 10 minutes:
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<form method="POST" action="/submit"> <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div> <button type="submit">Submit</button></form>Verify server-side:
const res = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ secret: process.env.TURNSTILE_SECRET, response: formData.get("cf-turnstile-response"), }),});const data = await res.json();if (!data.success) return 403;Honest take: it’s the easiest option, it works well, and it’s privacy-friendlier than the Google alternative. But it’s still a third-party dependency. If “no external calls” is a hard requirement, read on.
hCaptcha — Skip It for Self-Hosting
hCaptcha is popular, the API is drop-in compatible with reCAPTCHA v2, and it pays publishers. But it’s centralized, requires external calls, and there’s no self-hosted path. Mention it here for completeness: if Turnstile isn’t an option and you need a reCAPTCHA swap with minimal code changes, hCaptcha works. For a privacy-first homelab setup, though, it’s not the move.
Friendly Captcha — PoW With a Self-Hosted Enterprise Option
Friendly Captcha is a European (GDPR-compliant) proof-of-work CAPTCHA. The free tier hits their servers, but they offer an on-premises deployment for enterprise users. The PoW approach means users don’t have to solve puzzles — the browser grinds through a SHA-256 challenge in the background while the user fills out the form.
The self-hosted path requires a license and their on-prem server binary. If you’re running a small homelab, the cost probably doesn’t make sense. If you’re running infrastructure for an organization that has compliance requirements and an actual budget, it’s worth a look. The UX is genuinely good.
mCaptcha — Rust, FOSS, AGPL, No Excuses
mCaptcha is the one for people who want to host everything and have opinions about software licensing. It’s written in Rust, fully open source (AGPL-3.0), and uses proof-of-work. No third-party calls. You run the server, you own the data.
Spin it up with Docker:
services: mcaptcha: image: mcaptcha/mcaptcha:latest ports: - "7000:7000" environment: - MCAPTCHA_redis_url=redis://redis:6379 - MCAPTCHA_database_url=postgres://mcaptcha:password@postgres/mcaptcha - MCAPTCHA_server_domain=captcha.yourdomain.com - MCAPTCHA_captcha_gc=30 depends_on: - redis - postgres
redis: image: redis:7-alpine
postgres: image: postgres:16-alpine environment: POSTGRES_USER: mcaptcha POSTGRES_PASSWORD: password POSTGRES_DB: mcaptcha volumes: - mcaptcha_db:/var/lib/postgresql/data
volumes: mcaptcha_db:You get a web dashboard to manage sites, tune difficulty, and view stats. The widget embeds like any other CAPTCHA. Verification hits your own server.
Honest take: mCaptcha is solid if you want full control and the AGPL license works for your use case. The ecosystem is smaller than Altcha right now, and the docs assume you know what you’re doing.
Altcha — The Cleanest Self-Hosted PoW Option
Altcha is where I’d point most people who need a drop-in, fully self-hostable solution. BSD license, no telemetry, no third-party calls, works with any backend, and the widget is a single JavaScript file. The proof-of-work challenge is SHA-256 based and the difficulty is configurable.
The server component is a small Go binary (or you can use their npm package if you’re already in Node). Here’s a full setup with Caddy as the reverse proxy and a basic form backend.
docker-compose.yml:
services: altcha-api: image: altcha/altcha:latest environment: - ALTCHA_HMAC_KEY=your-secret-hmac-key-change-this - ALTCHA_MAX_NUMBER=1000000 ports: - "3000:3000"
caddy: image: caddy:2-alpine ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data depends_on: - altcha-api - app
app: image: your-app:latest expose: - "8080"
volumes: caddy_data:Caddyfile:
yourdomain.com { handle /altcha/* { reverse_proxy altcha-api:3000 }
handle { reverse_proxy app:8080 }}Frontend widget (drop into your HTML):
<script type="module" src="/altcha/altcha.js"></script>
<form method="POST" action="/submit"> <altcha-widget challengeurl="/altcha/api/v1/challenge" hidelogo ></altcha-widget> <button type="submit">Submit</button></form>Backend verification (Node.js example):
import { verifySolution } from "altcha-lib";
app.post("/submit", async (req, res) => { const payload = req.body.altcha; const valid = await verifySolution(payload, process.env.ALTCHA_HMAC_KEY); if (!valid) return res.status(400).send("Invalid CAPTCHA"); // process form...});The challenge is issued by your own server, solved in the browser, and verified by your own server. Nothing leaves your infrastructure. If you want floating difficulty (harder when you’re getting hammered, easier when it’s quiet), the API supports that too.
Anubis — The AI Scraper Bodyguard
This one’s different. Anubis (from TecharoHQ) isn’t a form CAPTCHA — it’s a proof-of-work gating reverse proxy. It sits in front of your entire service and challenges every client before they touch your app. In 2025, it exploded in popularity specifically because AI crawlers were hammering self-hosted Gitea/Forgejo instances, Wikis, and documentation sites into the ground.
The theory: AI scrapers and training data harvesters make massive parallel requests. Each request has to solve a PoW challenge. At scale, that’s computationally expensive for the crawler and cheap for you. Legitimate users solve it once in their browser in a second or two. Scrapers either give up or burn CPU trying.
Anubis also ships with a default allowlist of known AI crawler user agents that it blocks outright (GPTBot, ClaudeBot, etc.). You can tune this.
Here’s the real setup — Anubis in front of Forgejo:
docker-compose.yml:
services: anubis: image: ghcr.io/techarohq/anubis:latest ports: - "8080:8080" environment: - BIND=:8080 - DIFFICULTY=5 - UPSTREAM=http://forgejo:3000 - POLICY_FNAME=/config/policy.yaml volumes: - ./anubis-policy.yaml:/config/policy.yaml:ro depends_on: - forgejo
forgejo: image: codeberg.org/forgejo/forgejo:10 expose: - "3000" volumes: - forgejo_data:/data environment: - USER_UID=1000 - USER_GID=1000
volumes: forgejo_data:anubis-policy.yaml:
bots: - name: block-ai-scrapers user_agent_regex: "GPTBot|ClaudeBot|anthropic-ai|Amazonbot|Bytespider|PetalBot|SemrushBot" action: DENY
- name: allow-git-clients user_agent_regex: "git/" action: ALLOW
- name: allow-api-tokens path_regex: "^/api/v1/.*" action: ALLOW
rules: default_action: CHALLENGEThat config blocks known AI scrapers outright, passes git clients and API calls without challenge, and challenges everything else. Your SSH git remotes still work because they hit port 22, not 80/443.
Once a user solves the PoW (takes ~1-2 seconds), Anubis sets a cookie and they’re through. The challenge page is minimal and explains what’s happening — it’s not confusing to a real human.
DIFFICULTY is measured in leading zero bits on the SHA-256 hash. 4 is very easy, 5-6 is reasonable, 7+ starts adding real latency for legitimate users. For a personal Forgejo instance, 5 is fine.
Comparison Table
| Tool | Open Source | Self-Hostable | External Calls | Blocks AI Scrapers | Human Friction | License |
|---|---|---|---|---|---|---|
| Cloudflare Turnstile | No | No | Yes (CF) | Somewhat | Very low | Proprietary |
| hCaptcha | No | No | Yes | No | Low | Proprietary |
| Friendly Captcha | No | Enterprise only | Optional | No | Low | Proprietary |
| mCaptcha | Yes | Yes | None | No | Low (PoW) | AGPL-3.0 |
| Altcha | Yes | Yes | None | No | Low (PoW) | BSD-3 |
| Anubis | Yes | Yes | None | Yes (primary goal) | ~1-2s one-time | MIT |
| Privacy Pass | Yes (standard) | Partial | Depends | No | Near zero | IETF standard |
A Word on Privacy Pass
Privacy Pass is worth a brief mention because it’s where this space is heading. The idea: a user proves their humanity once (via some challenge, or even a browser attestation), then receives anonymous tokens they can spend to access services. No per-request challenges, no behavioral tracking, no correlation between requests.
Apple uses a version of this for iCloud Private Relay. Cloudflare supports it. But the self-hosted story is still immature — you’d be implementing a custom issuer and relying on client support that isn’t universal yet. Keep an eye on it. In a few years it might be the standard approach.
Should You Bother?
Depends on what problem you’re actually solving.
Just want to stop spam on a contact form? Altcha. It’s BSD-licensed, fully self-hosted, integrates in an afternoon, and your users won’t hate you. mCaptcha if you need a dashboard and AGPL works for you.
Getting hammered by AI scrapers on a Gitea/Forgejo/Wiki/documentation site? Anubis. Deploy it in front of your service and watch your server load drop. It was built for exactly this problem and it shows.
Already on Cloudflare and just need something that works today? Turnstile. No shame in it. It’s genuinely privacy-friendlier than reCAPTCHA and the integration is trivial.
Regulated environment, GDPR concerns, need on-prem? Friendly Captcha enterprise, or Altcha — both have a story here.
Running reCAPTCHA and wondering why you should change anything? Because every time a user fills out your contact form, Google learns something about them. You’re running your own infrastructure specifically to avoid this kind of thing. Might as well go all the way.
The tools exist. Your users’ data doesn’t have to be the cost of keeping bots out.