Rust CAPTCHA server with an iframe proof-of-work widget
mCaptcha is a self-hosted CAPTCHA server written in Rust. On a protected form, the visitor clicks a checkbox and their browser computes a SHA-256 proof of work inside a widget that the mCaptcha instance serves in an iframe. The instance checks the proof and returns a token, which the site's backend confirms with the instance before accepting the form. A dashboard handles accounts and site keys.
The amount of work goes up as the site's visitor count climbs. ALTCHA and Cap also make the browser pay in compute, ALTCHA as a library inside your existing backend, Cap either as a library or as a standalone server.
The comparison of self-hosted CAPTCHAs weighs what mCaptcha's proof of work stops against what the other tools stop.
Harder proofs under load, no cookies or per-IP limits
- Difficulty tied to traffic: each site key has a counter that goes up with every visit and back down after a cooldown period, and each visitor threshold has its own difficulty factor. The README promises no delay under moderate load and up to 2 seconds of computing under attack.
- Three levels in easy mode: enter your average traffic, the peak your site can handle and, optionally, the volume that once took it down. Default difficulty factors go from 50,000 to 5,000,000. Advanced mode lets you set each level by hand.
- Proofs can't be replayed: each proof-of-work configuration expires after 30 seconds and is good for one use.
- No cookies, no per-IP limits: CAPTCHA routes set no cookies according to the README, and the project site says it does not rate-limit by IP address, so visitors behind a NAT aren't penalized.
- A way through without JavaScript: a command-line tool solves the proof from the page URL, and the visitor pastes the resulting code into the form.
- Widget packages for plain JavaScript, React and Svelte, on npm under MIT or Apache-2.0, your pick.
Single release, idle repo, maintainer disillusioned with PoW
Postgres in the docs; Redis for running more than one instance. The bundled Compose file starts mCaptcha, Postgres 18 and mcaptcha/cache, a Redis build with a custom module that keeps the visitor counters. Without Redis, those counters live in the process's memory, which ties you to a single instance. The code also accepts MySQL or MariaDB, which the docs leave out. No memory or CPU figures are documented (issue #189, open since August 2025), and the published Docker images are amd64 only.
The README's credentials won't open the first account. The aaronsw account only exists in demo mode, which .env.docker-compose turns off along with registration, so you have to enable registration long enough to create your own account (#197).
Server-side verification doesn't follow reCAPTCHA's format. The project site says its APIs are compatible with reCAPTCHA and hCaptcha, yet the documented POST /api/v1/pow/siteverify takes token, key and secret as JSON and returns valid, where reCAPTCHA takes secret and response and returns success. Expect to rewrite the backend check. The HTML snippet in the docs also loads the widget's integration script from unpkg.com.
AGPL-3.0-or-later on the server, no paid tier. Anyone running a modified server as a network service has to publish its source. The project site lists no hosted plan and nothing to buy.
The maintainer has lost faith in proof of work. In June 2025, an analysis posted in issue #186 produced about 250 valid proofs per second on a single Ryzen 9 7950X at the hardest default level, while the server handled only about 650 requests per second carrying invalid proofs. The maintainer confirmed the findings in June 2026. He wrote that he is "disillusioned with PoW", which costs LLM crawlers next to nothing, and that he chose not to add defenses against SIMD or ASIC solvers because they would make the web worse for ordinary visitors. His plan is to update dependencies, ship a release, then design an alternative.
One release, no commits in a year. v0.1.0, from March 15, 2024, is still the only release. The last commit landed on October 7, 2025, and the latest image that the Compose file pulls dates from that same day, not from v0.1.0. The README still reads "active development". One developer wrote 761 of the 879 commits.
