Protection

5 min readUpdated

What is a proof-of-work challenge?

Quick answer

A proof-of-work challenge asks a client to perform a small computation and return the answer before it is allowed through. A real browser does this automatically in a fraction of a second with nothing to click, while a simple script cannot do it at all because it has no JavaScript engine. The point is not that the work is hard. It is that the cost falls on every new session, which is negligible for one visitor and ruinous for an attacker opening millions of them.

01How it works

The server returns a small page instead of the content, containing a puzzle. In the classic form, the browser must find a value that, hashed together with a seed the server issued, produces a hash starting with a required number of zero bits. There is no shortcut: the only method is to try values until one fits.

The browser runs that loop, submits the answer, and receives a token, usually in a cookie. Later requests present the token and skip the challenge, so the cost is paid once per session rather than per page. Implementations differ in the hash function, the token format and how long a token lasts, but this is the common core.

The idea, simplified
seed       = 'a91f...'        // issued by the server
difficulty = N                // chosen by the server

let n = 0;
while (!hash(seed + n).startsWith(zeros(difficulty))) n++;

// submit n: the server verifies it with a single hash

02Where the idea comes from

Computational puzzles as an anti-abuse tool go back to Cynthia Dwork and Moni Naor's 1992 work on combating junk mail. Adam Back proposed hashcash in 1997 and described it in his 2002 paper "Hashcash - A Denial of Service Counter-Measure": a sender searches for a hash with a set number of leading zero bits, and the recipient checks it with a single hash computation.

The same asymmetry drives today's web challenges. Open-source projects such as Anubis, released under the MIT licence, put browser challenges of this kind in front of websites specifically to slow the flood of automated requests from AI companies' crawlers, a form of web scraping that small sites struggle to absorb.

03Why it changes the economics

The asymmetry is the whole design. Solving costs a browser a brief computation once per session. Verifying costs the server a single hash. An attacker who wants to send a million requests without keeping sessions must solve a million puzzles, which means running a JavaScript engine, or an equivalent solver, a million times.

That turns a cheap attack into an expensive one. HTTP flood tooling is built to send requests as fast as possible with minimal per-request cost; requiring real work per request removes exactly the advantage the attacker was relying on.

Real visitorFlood tooling
Challenges to solveOne per sessionOne per request without a valid token
Needs a JS engineAlready has oneMust run one, or a solver, every time
Practical costA brief pause, onceFar more CPU than the attack was worth
Real browserProof of worksolve oncePassestoken issued< 1sFlood toolingProof of workper requestCost explodesattack stalls× millions
The same challenge costs a visitor one short computation and costs flood tooling one per request.

04Difficulty in numbers

Difficulty is the number of leading zero bits the hash must have, and the expected work doubles with every extra bit. That is plain probability, not a property of any product: a random hash has a one in 2 to the power of k chance of starting with k zero bits.

Hashcash used 20 bits as its standard stamp, about a million attempts. In a browser the wall-clock cost of a given difficulty depends heavily on the device, and that spread is the real constraint: optimised native code or a GPU computes hashes far faster than JavaScript on an old phone, so raising difficulty hurts the slowest real visitors long before it hurts a well-equipped attacker.

Memory-hard functions narrow that gap. Argon2, standardised in RFC 9106 for password hashing and proof-of-work applications, is designed to fill memory quickly, which specialised hardware cannot shortcut as easily as it can a plain hash.

Leading zero bitsExpected attemptsWork relative to 8 bits
82561×
1665,536256×
201,048,5764,096×
2416,777,21665,536×

05Compared with a CAPTCHA

A traditional CAPTCHA asks a person to do something: identify images, retype distorted characters, answer a simple sum, click a box. It works, but every legitimate visitor pays with an interaction. The W3C's note on the inaccessibility of CAPTCHA documents how visual and audio puzzles shut out people who are blind, deaf or dyslexic, while better recognition software keeps eroding the security they offer; it cites research that cracked Google's audio reCAPTCHA with a 90% success rate using Google's own speech recognition.

A proof-of-work challenge asks the browser instead of the person. Nothing is clicked and usually nothing is noticed beyond a brief delay. It does not prove humanity, since a headless browser passes, but it does prove that a full browser engine ran, which stops the large class of tooling that never runs one. That is why it sits comfortably inside broader bot management rather than replacing it.

06When a proof-of-work challenge fits

SituationGood fit?Why, or what to use instead
A flood of requests for HTML pagesYesEvery request without a valid token costs the sender real work
Login and sign-up formsYes, before the password checkAutomation pays per attempt instead of your database; see credential stuffing
JSON APIs used by apps and integrationsNoThese clients do not run pages; use keys, tokens and per-client limits
Search engine crawlersNoVerify them by reverse DNS or published ranges, as Google documents for Googlebot; see web crawlers
Uptime monitors and webhooksNoAuthenticate them or allow them explicitly
Headless-browser botsPartlyThey can solve it; behaviour, rate and reputation have to do the rest

07What it does not do

  • It does not prove a human is present: a headless browser solves it like any other browser
  • It does not stop a determined attacker with hardware to spare; it makes every request cost them, which is the realistic goal
  • It cannot help clients that never run JavaScript, so APIs and machine clients need their own controls
  • A solved token is only as strong as its lifetime and scope: a long-lived token that works anywhere can be shared across many machines
  • It costs the slowest real devices the most, which is why difficulty cannot simply be turned up

Questions

Frequently asked questions

No. It proves a JavaScript engine ran. A headless browser passes, which is why it is one layer among several rather than a complete answer.

Usually a short pause, once per session. The exact time depends on the device and the difficulty, and users on old phones notice it most, which is why difficulty is a trade-off rather than a dial to turn up.

They should not, as long as the site verifies crawlers properly. Google documents two methods for Googlebot: a reverse DNS lookup confirmed by a forward lookup, or matching the address against its published IP ranges. The user-agent string alone proves nothing.

No, and that is deliberate: a browser challenge needs a browser. Clients that legitimately cannot run one, such as API consumers and monitoring tools, need a different form of verification, such as an API key, a token or an explicit allow rule.

Per visitor the work is small and paid once per session, and it costs far less human time than image puzzles. The real cost lands on clients that send far more requests than any person would, which is the point.

Sources

Sources and verification

Expected-attempt figures computed as 2 to the power of the number of leading zero bits.

  1. 01Hashcash - A Denial of Service Counter-Measure (Adam Back, 2002) · Satoshi Nakamoto Institute
  2. 02Hashcash · Wikipedia
  3. 03Inaccessibility of CAPTCHA: Alternatives to Visual Turing Tests on the Web · W3C
  4. 04Anubis (TecharoHQ/anubis) · Techaro
  5. 05RFC 9106: Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications · IETF
  6. 06Verify requests from Google crawlers and fetchers · Google for Developers

Last updated