The proof-of-work renaissance: how Kasada, hCaptcha, and Anubis use compute as a tax
A real browser solves it in about a second and never notices. A scraper that wants to hit the same page ten thousand times has to solve it ten thousand times, and that is the entire idea. Make the client pay a small, unavoidable compute cost before it gets a response, and the cost barely registers for one human reading one page but compounds into something real for a bot fetching millions. The defender’s side of the ledger stays cheap: handing out a puzzle and checking an answer is microseconds of work. The attacker’s side is forced to brute-force a hash. That gap, hard to produce and trivial to verify, is the oldest trick in the anti-abuse book, and after two decades of sitting mostly idle it is back in production at companies most people have never heard of and on FOSS git servers most people use every day.
The question this post tries to answer is narrow and worth being precise about. Does it actually work? Proof-of-work shows up today in three very different places: a commercial bot-defense vendor (Kasada), a CAPTCHA company that bolts it under an image challenge (hCaptcha), and a wave of small open-source proxies and widgets aimed at AI scrapers (Anubis, mCaptcha, and friends). They share one primitive and almost nothing else, and the honest verdict differs sharply depending on which job you ask the primitive to do. The friction is real. The wall is not. The most cited critique of the open-source variants makes a compelling case that the math doesn’t add up against a motivated attacker, and the most successful commercial use treats the compute tax as one signal among many rather than the whole defense.
Below: the Hashcash origin and why the asymmetry holds; the shape of a generic PoW challenge; how Anubis and mCaptcha apply it to AI scrapers; how Kasada and hCaptcha fold it into a larger detection stack; the economics, including the native-solver problem that breaks the open-source case; and where the primitive genuinely earns its keep.
The idea, and where it came from
Proof-of-work as an anti-abuse tool predates Bitcoin by more than a decade. Adam Back proposed Hashcash in 1997 and described it formally in a 2002 paper titled “Hashcash - A Denial of Service Counter-Measure.” The target then was email spam and abuse of un-metered resources like anonymous remailers. The mechanism: before you send a message, you compute a stamp by finding a header whose hash has a required number of leading zero bits. The recipient checks the stamp with a single hash. Sending one email costs you a second of CPU. Sending a million costs you a million seconds, which is the point.
The trick is a partial hash preimage. You take some fixed input (in Hashcash, a header containing the recipient, a date, and a random string), append a counter, and hash the whole thing. Most hashes look random. You keep incrementing the counter and rehashing until the output happens to start with, say, 20 zero bits. There is no shortcut. A cryptographic hash gives you no way to work backward from “I want 20 leading zeros” to the counter that produces them, so you try counters until one lands. With 20 required zero bits the chance any single attempt works is 1 in 2^20, roughly one in a million, so on average you do about a million hashes. Hashcash defaulted to 20 bits and used SHA-1. Verification is one hash and a look at the leading bits, about two microseconds on a 1 GHz machine of the era.
*The asymmetry the whole primitive rests on: finding a nonce is exponential in the difficulty N, verifying it is a single hash.*Two properties make this useful and two make it fragile, and both pairs matter for everything that follows. It is useful because the cost is asymmetric and because difficulty is a single tunable knob: add one required zero bit and you double the attacker’s expected work while your verification cost stays flat. It is fragile because the cost is paid in raw hashing, which is exactly the thing specialized hardware does best, and because it taxes everyone equally regardless of intent. Back himself noted the first problem: Moore’s Law erodes the deterrent over time, and ASIC miners can run three orders of magnitude faster than a consumer CPU. The second problem is the one the modern anti-scraper deployments keep running into, and we will come back to it.
Satoshi Nakamoto cited Hashcash in the Bitcoin white paper, which is how most engineers first met the idea. Bitcoin took the same partial-preimage puzzle, swapped SHA-1 for double SHA-256, and pointed it at consensus instead of spam. The anti-bot use is closer to Back’s original intent than to Bitcoin’s: a rate limiter, not a lottery, with the cost calibrated so an honest client barely feels it.
What a PoW challenge looks like on the wire
Strip away the branding and every anti-bot PoW system has the same three-step shape. The server issues a challenge. The client searches for a solution. The client submits the solution, and the server checks it. The differences are all in what goes into the challenge, how difficulty is set, and what the valid solution buys you.
ALTCHA, an open-source PoW CAPTCHA, documents its design cleanly enough to use as a reference for the family. The server generates a random salt (at least ten characters) and a hidden secret number, concatenates them, and hashes the result with SHA-256 to produce the challenge. It also computes an HMAC signature over the challenge using a server-side key. The client receives the salt and the challenge but not the secret number. It iterates from zero, concatenating the salt with each candidate number and hashing, until its hash equals the challenge. Then it sends back the salt, the number it found, and the signature. The server re-derives the challenge from the salt and the submitted number, confirms it matches, re-derives the HMAC and confirms that too, and only then treats the request as verified. The signature is what stops a client from inventing its own easy challenge: the server only honors challenges it signed.
That signature detail is the difference between a toy and something deployable. A naive PoW where the client both picks the puzzle and solves it proves nothing, because the client can pick a puzzle with difficulty zero. The server has to bind the challenge to its own secret, set the difficulty itself, and give the solution a short, single-use lifetime. mCaptcha, another open-source PoW system written in Rust, makes the replay protection explicit: its proof-of-work configurations have a short lifetime, on the order of 30 seconds, and can be used only once. Submit a solution against an expired or already-used configuration and it is rejected. Without that, one solved challenge becomes a reusable skeleton key and the tax collapses to a single payment.
The other design lever is variable difficulty. A fixed difficulty has to be set for the worst case, which over-taxes honest users during calm periods and under-taxes attackers during a flood. mCaptcha scales difficulty with server load: no delay under moderate load, up to a couple of seconds under attack. The deterrent rises precisely when an attack is in progress and relaxes when traffic is normal, which is the behavior you want from a tax meant to price out bulk abuse without punishing the median visitor. Friendly Captcha pushes the same idea a step further by splitting one hard puzzle into several easier ones to reduce the variance in solve time, since a single high-difficulty puzzle can occasionally take an unlucky client far longer than average.
Anubis: proof-of-work against the AI crawl
The clearest recent case for PoW as a standalone defense is Anubis, and it is also the clearest case against it. Anubis is an MIT-licensed reverse proxy written by Xe Iaso, first released on 19 January 2025, built for one specific problem: AI training crawlers hammering FOSS infrastructure while ignoring robots.txt, user-agent blocks, and X-Robots-Tag headers. The author’s framing leans on Egyptian mythology, weighing the soul of each request before it reaches the origin, and the loading screen ships with commissioned anime jackal-girl art. Under the costume it is Hashcash.
The mechanics are close to the textbook version. When a client whose User-Agent contains “Mozilla” requests a protected page, Anubis serves a challenge: a string plus a difficulty parameter. The browser, using a Web Worker and the Web Crypto API, hashes the challenge with an incrementing nonce until the SHA-256 output has the required number of leading zeros. The default difficulty is five. Solve it and Anubis signs a JWT, drops it in a cookie valid for a week, and waves you through until the cookie expires. The challenge itself is derived from a SHA-256 sum over request attributes that includes the Accept-Encoding, Accept-Language, and X-Real-Ip headers, the User-Agent, the current UTC time rounded to the nearest week, and a fingerprint of the instance’s ED25519 private key. Each Anubis instance generates its own ED25519 keypair at startup, so a token minted for one site is meaningless at another.
*Anubis in one round trip. The challenge binds to request headers and the instance's ED25519 key; the reward is a week-long signed cookie.*The adoption tells you the pain was real. By 2026 Anubis sat in front of the GNOME Project’s GitLab, the Linux kernel mailing-list archives and git server, FFmpeg, Wine, ScummVM, FreeCAD, OpenWRT, UNESCO, and Duke University’s digital repositories, among many others. For operators of small FOSS infrastructure with no Cloudflare contract and a server melting under crawler load, a single Go binary that drops in as a reverse proxy and costs nothing was an easy yes. And for the bulk of badly-behaved crawlers, the ones that fetch URLs with a plain HTTP library and no JavaScript engine, it works for a reason that has little to do with the hashing: those clients cannot run the Web Worker that solves the challenge at all, so they bounce off the gate before any compute question arises. The real filter, at least at first, was “can you execute JavaScript,” and the PoW was the thing the JavaScript happened to compute.
That distinction is where the design gets interesting and where it starts to come apart.
The native-solver problem
In August 2025, security researcher Tavis Ormandy published a dismantling of the Anubis cost model, and it is the most important document in this whole topic because it forces the question of what the tax actually costs the people it is meant to deter. His benchmark was a free-tier GCP e2-micro instance, the smallest commodity machine Google rents, which managed roughly 2^21 SHA-256 operations per second. From that he worked out the cost of minting valid tokens for every Anubis deployment he could find. A difficulty-5 challenge needs about 2^16 hashes on average per solve. Across the full population of deployments, generating a token for each one came to roughly six minutes of compute on that single free VM. His blunt summary: the cost of unrestricted crawler access for a week is approximately zero, and you do not reach a single cent of monthly compute cost until several million sites have deployed Anubis. His proof-of-concept solver, twenty-five lines of C, mined a token in about 0.017 seconds.
The core of the critique is not that the hashing is too cheap in absolute terms. It is that it is cheap for exactly the wrong party. The browser solves the puzzle in JavaScript through the Web Crypto API, which is slow. A native solver does not. Independent measurements put a multi-threaded C implementation around 200 times faster than the browser’s SubtleCrypto, and a WebAssembly solver around 140 times faster. So the honest human running the official client pays the full tax, while the AI vendor with a datacenter full of CPUs and 25 lines of C pays a rounding error. Ormandy’s phrasing is that the scheme has the problem backwards, effectively limiting access to those without resources while waving through those with them. A tax that falls hardest on the people you want to keep and lightest on the people you want to repel is not a tax that works.
A separate benchmark on real hardware fills in the human side of that ledger. On an Intel Core Ultra 7 165H, a difficulty-4 Anubis challenge took about 1,349 milliseconds in headless Chromium, working through roughly 118,000 SHA-256 hashes at about 87,600 hashes per second. Pushed to difficulty 8 the same machine needed about eleven seconds, and difficulty 10 ran to nearly two minutes. The author’s read was that 1.35 seconds per request imposes practical friction against commodity scraping tools even though, per Ormandy, it is theoretically cheap to a determined attacker. The gap between “theoretically cheap” and “practically annoying” is the entire operating range of the open-source PoW deployments, and it is narrow.
It got narrower in public. Also in August 2025, Codeberg, which runs the Forgejo git-hosting software, reported that AI crawlers had bypassed its Anubis deployment. A staff member’s summary was that the crawlers had learned to solve the challenges, with some of the traffic appearing to originate on networks operated by a China-based telecom. The bots had simply started executing the JavaScript, or running a native solver, and once they did the PoW gate stopped gating. Codeberg’s own conclusion was not that Anubis is useless, but that it is one layer among several, and they began looking at complementary tools. That is the right read. The proof-of-work in Anubis raised the floor on crawler behavior for a while, forced the laziest bots out, and bought small operators breathing room. It did not, and given the cost asymmetry could not, hold against an attacker willing to spend twenty-five lines of effort.
If you want the texture of how PoW sits alongside other anti-bot mechanics, the contrast with server-side vs client-side bot detection is worth holding in mind here: Anubis makes its decision almost entirely client-side, on a single signal, which is exactly why a client that lies about that signal walks straight through.
Kasada: compute cost as one tooth on a larger gear
The commercial use of PoW looks nothing like the open-source one, and the difference is instructive. Kasada, an anti-bot vendor founded in Sydney in 2015, builds a client SDK (the KPSDK) whose job is to decide on the first request whether a client is a real browser. Proof-of-work is in there. It is not the defense.
When a page protected by Kasada loads, the browser fetches an obfuscated script, often served from a path like /ips.js, that contains a custom virtual machine plus bytecode for that VM to run. Among the things the VM does is a proof-of-work computation, and the timing here is the opposite of Anubis: where the open-source variants want the puzzle to take a noticeable fraction of a second, Kasada’s takes a real browser only a couple of milliseconds, verifiable server-side in microseconds. The output, together with a large pile of fingerprinting signals, gets encrypted and posted back, and Kasada issues the headers it expects on every subsequent request: x-kpsdk-ct, x-kpsdk-cd, and a version pin x-kpsdk-v. Practitioner write-ups describe CT as an expensive session token that can be reused and CD as a cheap per-request token that is single-use, with the computational challenge re-issued through the session so the tokens need ongoing renewal. The internal field layout of those tokens is not published by Kasada, and the details here come from independent reverse-engineering, not vendor documentation. Our teardown of the KPSDK token and VM goes deeper on that machinery.
The point for this post is why the PoW is so much cheaper than Anubis’s and what it is for. Kasada is not trying to price out bulk crawling with the hash cost alone. A two-millisecond puzzle imposes almost no tax. What it does is force the client into the VM, where the real work happens: the anti-instrumentation checks that hunt for Playwright, CDP, patched runtimes, and headless tells, plus device and runtime fingerprinting. The PoW is a forcing function and a tamper check, not the wall. It guarantees that any client producing a valid token actually ran the VM, in order, with the fingerprinting intact, because the proof and the signals are bound together in the same encrypted payload. Solve the hash without running the rest and you have nothing. The asymmetry Kasada cares about is not “hashing costs the attacker more than me.” It is “to forge one valid token you have to reverse and re-implement an obfuscated VM, and we rotate that VM.”
That reframes the cost entirely. The expensive thing is not the SHA-256 loop. It is the engineering effort to produce a correct payload at all, renewed every time Kasada ships a new build. Compute-as-a-tax becomes compute-as-an-attestation: the puzzle exists so that the answer can only have come from a client that did everything else you demanded. This is also why Kasada returns a hard block, typically a 429 or 403, rather than a puzzle a human has to look at. There is no CAPTCHA image. The proof-of-work is invisible, and a human never sees the tax at all.
hCaptcha and the captcha family
hCaptcha sits between these two worlds, and it is the one place where you should be most careful about what is actually documented. hCaptcha is an image-CAPTCHA company from Intuition Machines, marketed as a drop-in reCAPTCHA replacement, and it does ship a proof-of-work component. But hCaptcha’s own developer documentation does not describe it. The public docs explain the visible flow (the user clicks a checkbox or solves an image grid, receives a token, and your backend verifies that token by POSTing it to https://api.hcaptcha.com/siteverify) and stop there. They do not mention proof-of-work, computational challenges, or the internals at all. So everything specific about hCaptcha’s PoW comes from third-party reverse-engineering, and that body of work consistently describes a WebAssembly component that consumes a set of browser signals and produces a token (commonly written hsw in that literature) which the official client computes before it can submit a result. Treat the precise field names there as inferred from observed traffic and solver projects, not as a vendor spec, because the vendor has not published one.
What you can say with confidence is the role the PoW plays, because it is structurally the same role it plays in Kasada. hCaptcha already has a strong human-or-not signal from the image challenge and from the behavioral and device telemetry it collects around the widget. The proof-of-work is a cost multiplier layered on top of that verdict: a client flagged as suspicious can be handed a harder computation, so that even a working solver pays an escalating compute price as hCaptcha’s confidence that it is automated rises. The image challenge separates likely humans from likely bots; the PoW makes being a bot expensive in proportion to how bot-like you look. The detail of how that sits within the wider hCaptcha flow is covered in hCaptcha’s challenge pipeline, and the broader detection-design contrast in hCaptcha vs reCAPTCHA.
The pure-PoW CAPTCHAs, the ones that do publish their designs, show the primitive working at the edge of its comfort zone. ALTCHA, mCaptcha, Friendly Captcha, and Cap all replace the image puzzle entirely with an invisible hash search, trading the privacy and accessibility problems of image CAPTCHAs for a compute tax. They are honest about being rate limiters rather than walls. mCaptcha frames itself as exactly that: it does not use IP for rate-limiting (so it works behind NAT), it scales difficulty with load, and it expects a slight delay under attack rather than a hard stop. These systems work best in the spam-and-flood role Back originally designed Hashcash for, where the goal is to make a bulk-submission attack uneconomical rather than to identify any individual client. They are weakest exactly where Anubis is weak: a single determined attacker with a native solver pays little, because the tax is uniform and the work is plain hashing.
*The fault line in modern PoW: whether the puzzle is the defense or merely the receipt for having run the real defense.*Cloudflare’s Turnstile belongs in the attestation camp too, and it states the role plainly. Turnstile runs a series of small non-interactive JavaScript challenges to gather signals about the browser, and Cloudflare lists proof-of-work (computational puzzles) and proof-of-space alongside web-API probing and browser-quirk detection as part of that battery. The PoW is one item on a checklist of environmental probes, not the gate. The full machinery is in Cloudflare Turnstile internals. The pattern is consistent across every well-resourced vendor: nobody who can afford a full detection stack trusts the hash cost to do the work on its own.
The economics, stated plainly
It helps to write the ledger out, because the disagreements about PoW almost always come from comparing the wrong two numbers. There are three costs in play and they move independently. There is the defender’s verification cost, which is one hash, effectively free and constant no matter what. There is the honest client’s cost, paid in slow browser JavaScript, which is the friction a real user feels and the thing that breaks accessibility for old or low-power devices. And there is the attacker’s cost, paid in fast native or WASM code, which is the deterrent you actually care about. The whole scheme works only if the attacker’s cost dominates, and the native-solver gap means it usually does not. The honest client runs SubtleCrypto. The attacker runs C that is two orders of magnitude faster. You are taxing the wrong column.
Difficulty tuning cannot fix this, only relocate it. Raise the difficulty until a native solver feels it and the honest browser is now spending many seconds per page, which for a single human reading one article is merely annoying but for a site with real traffic and real low-end devices is a usability disaster. Lower it until the honest browser is comfortable and the native solver is back to paying nothing. There is no setting where the puzzle is cheap for humans and expensive for a well-engineered bot, because the difference between them is not how hard the puzzle is. It is how fast their code runs. This is the structural reason the standalone-PoW camp keeps losing to motivated attackers and the structural reason the attestation camp ties the puzzle to something a fast solver cannot fake. The economics of where this lands commercially are their own subject, taken up in the economics of anti-bot vendors.
The one place the economics clearly favor the defender is the flash crowd. A sudden flood of cheap requests, a credential-stuffing run or a scripted checkout rush, is exactly the case load-scaled difficulty was built for. When mCaptcha ramps difficulty under load, it is not trying to identify any bot. It is making the marginal request more expensive than the marginal response, so that a flood prices itself out while the trickle of honest traffic pays almost nothing. In that role the uniformity of the tax stops being a flaw, because you are not trying to tell humans from bots at all. You are just metering a resource that was previously free, which is precisely the problem Hashcash set out to solve in 1997.
Where it actually earns its keep
The proof-of-work renaissance is real but its press is wrong in both directions. The open-source AI-scraper proxies got written up as a clever new weapon against crawlers, and the security researchers wrote back that the weapon is theater. Both overstate. Anubis and its kin demonstrably cleared the lazy, no-JavaScript crawlers off small infrastructure for a meaningful stretch in 2025, which for an unfunded FOSS project drowning in bot traffic was worth far more than the twenty-five lines of C it eventually cost an attacker to defeat. That is a genuine, if temporary, win. It is also a ceiling, and the Codeberg bypass found it within months. A single tunable knob over a single signal is a speed bump, and speed bumps work right up until someone decides to drive over them.
The durable use of the primitive is the quiet one. As an attestation that a client ran the code you served, bound cryptographically to the fingerprints and instrumentation checks that do the real discrimination, proof-of-work is load-bearing inside Kasada, hCaptcha, Turnstile, and every serious vendor’s client. There the hash cost is almost beside the point; the value is that a valid proof can only come from a client that executed your VM end to end, and that the proof rots fast enough to deny replay. The same SHA-256 loop Adam Back proposed against spam in 1997 is doing useful work in 2026, not as a wall a bot cannot climb, but as a receipt a bot cannot forge without doing everything else you asked first. The honest line on the renaissance is that the primitive came back, did less than its boosters claimed against a determined adversary, and quietly became indispensable in the one role nobody put on the poster.
Sources & further reading
- Adam Back (2002), Hashcash - A Denial of Service Counter-Measure — the original PoW paper; partial-hash-preimage stamps, the cost asymmetry, and the spam/DoS use case.
- Wikipedia (2026), Hashcash — the 1997 proposal and 2002 formalization, SHA-1, 20 default zero bits, the X-Hashcash stamp format, and Bitcoin’s reuse.
- Xe Iaso (2025), Block AI scrapers with Anubis — the author’s own account of the design: SHA-256 hashcash, the header-derived challenge, the ED25519 key, and the JWT cookie.
- Wikipedia (2026), Anubis (software) — release date, MIT license, difficulty defaults, the adopter list, and the documented limitations.
- Tavis Ormandy (2025), Anubis — the cost-model critique: the GCP free-tier benchmark, the 25-line C solver, and the argument that the tax falls on the wrong party.
- Mike Bommarito (2025), Anubis benchmark: measuring proof-of-work overhead in headless chromium — measured solve times and hash rates across difficulty levels on real hardware.
- The Register (2025), Codeberg beset by AI bots that now bypass Anubis defense — the August 2025 report that crawlers had learned to solve the challenge.
- mCaptcha (2026), mCaptcha: a no-nonsense CAPTCHA system — SHA-256 PoW, load-scaled difficulty, 30-second single-use configurations, and the privacy/NAT design.
- ALTCHA (2026), Proof of Work Mechanism — a cleanly documented PoW spec: salt, hidden number, SHA-256 challenge, and the HMAC signature that binds it.
- Friendly Captcha (2026), Proof-of-Work CAPTCHA — the Web Worker puzzle, risk-scaled difficulty, and the multi-puzzle variance-reduction trick.
- Cloudflare (2026), Turnstile challenge types — Turnstile’s non-interactive challenges, with proof-of-work and proof-of-space listed among the signals it gathers.
- Kasada (2026), Product — vendor positioning for the KPSDK as invisible, hard-block bot defense rather than a visible puzzle.
Further reading
DataDome's detection model: every signal it collects on the first request
Traces what DataDome evaluates on the very first request, before any JavaScript runs: the TLS/JA4 fingerprint, the HTTP/2 frame profile, the header set, and IP and ASN reputation, and how those signals stack into one decision.
·19 min readDataDome's server-side scoring pipeline: from edge to decision in milliseconds
Traces how DataDome turns an HTTP request into an allow, challenge, or block verdict at the edge: the module-to-API split, the form fields it ships, the regional inference layer, and the latency budget that keeps it synchronous.
·22 min readThe DataDome cookie lifecycle: token issuance, rotation, and validation
Traces the datadome cookie end to end: how it is issued after a challenge, what the 128-byte token encodes, when it rotates, how long it lives, and how the edge validates it on every request through the Protection API.
·22 min read