Skip to content
Go back

Self-Hosted CAPTCHA Alternatives in 2026

By SumGuy 10 min read
Self-Hosted CAPTCHA Alternatives in 2026

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:


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: CHALLENGE

That 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

ToolOpen SourceSelf-HostableExternal CallsBlocks AI ScrapersHuman FrictionLicense
Cloudflare TurnstileNoNoYes (CF)SomewhatVery lowProprietary
hCaptchaNoNoYesNoLowProprietary
Friendly CaptchaNoEnterprise onlyOptionalNoLowProprietary
mCaptchaYesYesNoneNoLow (PoW)AGPL-3.0
AltchaYesYesNoneNoLow (PoW)BSD-3
AnubisYesYesNoneYes (primary goal)~1-2s one-timeMIT
Privacy PassYes (standard)PartialDependsNoNear zeroIETF 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.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
ddrescue vs TestDisk vs PhotoRec

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts