If you have built anything that browses the web for a user, you have met the wall: your agent fills a form, submits it, and gets a CAPTCHA or a Cloudflare interstitial no headless browser is meant to pass. As of Cloudflare's June 2026 Bot Management update, there is now a lane around that wall — but you only get into it if your agent proves who it is. This is the how-to for getting in: generate one keypair, publish it, and sign every request with Web Bot Auth. It is roughly forty lines of code, and the big agent makers already do it.

The one-paragraph version#

Cloudflare added a rule action called Challenge Agent. When a request carries a valid Web Bot Auth signature, an endpoint that used to serve a human CAPTCHA instead serves a signed-token check your agent can pass programmatically. Signing is the price of admission. Web Bot Auth is an IETF-draft standard that profiles RFC 9421 HTTP Message Signatures for agent traffic: an Ed25519 key per operator, a public JWKS directory on a domain you control, and three headers added to each request. It proves who is calling — it does not grant access. A site can still say no; it just can't confuse you with a scraper anymore.

1. Generate the keypair#

The identity is an Ed25519 keypair. Generate it once, keep the private half in your secret store, and never ship it inside the agent binary.

import { generateKeyPair, exportJWK } from 'jose';

const { publicKey, privateKey } = await generateKeyPair('Ed25519', {
  extractable: true,
});

const publicJwk = await exportJWK(publicKey);   // publish this
const privateJwk = await exportJWK(privateKey);  // guard this
// keyid is the RFC 7638 JWK thumbprint of the PUBLIC key

2. Publish the JWKS directory — this is your identity#

Serve the public key as a JSON Web Key Set at a well-known path on a domain you control. That URL, not a User-Agent string, is what a verifier resolves to learn who you are.

GET https://youragent.example/.well-known/http-message-signatures-directory
{ "keys": [ { "kty": "OKP", "crv": "Ed25519",
             "x": "<base64url public key>", "kid": "<jwk-thumbprint>" } ] }

Two rules that get sloppy in practice and shouldn't: serve this over HTTPS on a stable domain, and plan key rotation before you launch. Publish the next key in the set before you retire the old one, so in-flight signatures still verify.

3. Sign every request with three headers#

Each outbound request gets a signature over its components per RFC 9421, tagged for Web Bot Auth. Cloudflare's reference implementation does the byte-exact base construction — use it rather than hand-rolling the canonicalization.

import { signRequest } from 'web-bot-auth';

const signed = await signRequest(request, {
  privateKey: privateJwk,
  keyid: thumbprint,                 // JWK thumbprint of your public key
  tag: 'web-bot-auth',
  created: Math.floor(Date.now() / 1000),
  expires: Math.floor(Date.now() / 1000) + 300,
  nonce: crypto.randomUUID(),        // reject-on-reuse at the verifier
});
// Adds three headers to the request:
//   Signature-Input: what's signed + tag, keyid, created/expires, nonce
//   Signature:       the Ed25519 signature over that base
//   Signature-Agent: https://youragent.example  (points at your JWKS)

The verifier follows Signature-Agent to your directory, selects the key named by keyid, rebuilds the base from Signature-Input, checks the signature, and rejects a reused nonce or an expired timestamp. If it passes, the site knows two hard facts: the request came from the holder of that private key, and the key belongs to your domain.

You are not proving you're human. You're proving you're a specific, accountable bot — and that is exactly the thing a CAPTCHA can't ask for.

4. What you get, and what you still don't#

On a Cloudflare-fronted site with a Challenge Agent rule, your signed request now meets a token check instead of a CAPTCHA. AWS ships the same mechanism the other direction — its Bedrock AgentCore Browser auto-signs to reduce CAPTCHA friction — and Akamai and AWS WAF verify the same draft. Cloudflare's launch Verified AI Agent category already lists the browser agents most of your users run.

Be clear-eyed about the ceiling. A signature is identity, not permission: a site can still refuse a verified agent, throttle it, or demand a paid key. It says nothing about whether your agent behaves — get caught scraping abusively under a clean key and the light lane closes, because now they know exactly whose key to revoke. And it does nothing for an agent acting as a logged-in human, which already looks like one. Sign anyway. The web spent two decades getting better at annoying humans to keep automation out; this is the first move that lets the accountable automation in on purpose, and being on the wrong side of it means eating the same CAPTCHAs as the scrapers you're nothing like.