Answers · AI crawlers
How do I sign my own AI agent's HTTP requests with Web Bot Auth so websites can verify it?
Last updated
TL;DR: Give the agent its own Ed25519 key, publish the public half as a JSON Web Key Set at /.well-known/http-message-signatures-directory on a domain you control (HTTPS, media type application/http-message-signatures-directory+json, and sign that response with the key so nobody can mirror your directory), then sign every outgoing request with HTTP Message Signatures (RFC 9421). Each request carries three headers: Signature-Agent (where your keys are), Signature-Input (covering at least @authority and signature-agent, with tag="web-bot-auth", keyid set to the key's JWK thumbprint, created and a short expires) and Signature. Sites that check can then tell your agent from anything borrowing its user-agent string. Two details trip people up. Cloudflare's docs (updated Oct 8, 2026) say its verifier fails a request that sends the newer dictionary form of Signature-Agent (sig1="https://...") and wants a quoted bare string, while the working-group draft published October 29, 2026 (draft-ietf-webbotauth-httpsig-protocol-01) says signers MUST send the dictionary form and verifiers MAY still accept the old one. And Cloudflare only trusts your key after you register the directory through its bot submission form. Test against each verifier you care about before rollout; gate_check verifies either header form for free.
What should I check, step by step?
1. Generate a dedicated Ed25519 key for the agent (Cloudflare's docs use openssl genpkey -algorithm ed25519). Keep the private key in a secret store, one key per agent or environment, and never reuse a TLS or code-signing key.
- Publish a JWKS with the public key (kty OKP, crv Ed25519, x) at https://<your domain>/.well-known/http-message-signatures-directory, served with Content-Type application/http-message-signatures-directory+json. Never include the private d parameter.
- Sign the directory response itself: one signature per key, tag="http-message-signatures-directory", keyid = that key's thumbprint, covering "@authority";req. Cloudflare says this stops anyone mirroring your directory and registering it as theirs.
- Compute the keyid as the base64url JWK SHA-256 thumbprint (RFC 7638; for Ed25519 the OKP form in RFC 8037). If you put a kid in the JWKS, the draft says it must equal that thumbprint.
- On each request, cover at least @authority and signature-agent, plus @method and @path if you want the signature to fit one request only. Set created, a short expires (Cloudflare suggests a minute is often enough; the draft recommends no more than 24 hours) and a fresh nonce.
- Choose the Signature-Agent form per verifier: a quoted string such as "https://agent.example.com" for Cloudflare today, the dictionary form sig1="https://..." for verifiers that follow the current draft. Don't add parameters to dictionary members; the draft says verifiers must ignore a member that carries them.
- Sign only ASCII components, and don't sign Content-Digest if a proxy might change the body. Cloudflare fails @query-params, @status and the sf, bs, key and req parameters on request components.
- Register with the verifiers you need (Cloudflare: BotBase submission form, Verification Method = Request Signature, your directory URL), then test: crawltest.com/cdn-cgi/web-bot-auth returns 401 for a well-formed request with an unknown key, 200 when the key is known and the signature verifies, 400 otherwise.
- Rotate by publishing the new key next to the old one, switching signing, then removing the old key once in-flight signatures have expired.
What does a Web Bot Auth signature prove to a website about an AI agent?
A user-agent string is text anyone can send. A valid Web Bot Auth signature shows the request was signed by a key published at the Signature-Agent URL, so a site can allowlist, rate limit or block that signer without relying on IP ranges. Cloudflare's verified bots page says signed agents have been counted as Verified since July 1, 2026, labelled Direct (one operator) or Intermediary (a service many end users drive).
- It doesn't prove the agent is well behaved. Cloudflare's second bar for Verified status is behaviour: obey robots.txt and crawl directives, keep request rates reasonable, don't evade site owner preferences. A signature makes your agent accountable for what it does, which is the point.
- The draft also warns that a signature covering only @authority is valid for any method, path or body sent to that site until it expires, so anyone who sees one request can reuse it there. Short expiry and covering @method and @path narrow that.
What is the status of the Web Bot Auth standard and its Signature-Agent header format?
The IETF webbotauth working group adopted the protocol as draft-ietf-webbotauth-httpsig-protocol (revision 01, October 29, 2026). It replaced the individual drafts Cloudflare's docs still cite, and it is still a draft, so details can change.
- The main incompatibility today is the Signature-Agent header: Cloudflare implements the older quoted-string form from draft-meunier-http-message-signatures-directory-03; the WG draft requires signers to send a dictionary keyed by signature label. Pick per destination until verifiers converge, and re-test when either side updates.
- Not every operator can host a directory. draft-singh-webbotauth-hosted-directories-00 (July 2026) discusses directories a registry hosts for many agents, and notes that a host must not hold tenants' private keys, so it can't sign the directory response the way a self-hosted one can. Some verifiers, including Cloudflare's registration, expect the signed self-hosted form.
How do I test that my AI agent's signed requests verify before rollout?
Capture one real outgoing request (method, URL and the three signature headers exactly as sent) and run it through each verifier you care about: Cloudflare's crawltest endpoint, your own middleware, and gate_check.
- gate_check resolves your Signature-Agent URL over HTTPS (no redirects), picks the key by thumbprint, verifies the signature and checks it was made for the site's domain, then answers allow or deny with reasons such as an expired signature or a directory it couldn't fetch. It accepts both the quoted-string and dictionary forms of Signature-Agent.
How do I ask Rightshold about this (free, no key)?
Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Here is one request my agent just sent, with its Web Bot Auth headers exactly as sent. Does the signature verify against my published key directory for this site, and would a site that only allows my signer let it through?”
{
"tool": "gate_check",
"arguments": {
"policy": {
"domain": "shop.example.com",
"require_web_bot_auth": true,
"allowed_signature_agents": [
"agent.example.com"
]
},
"request": {
"method": "GET",
"url": "https://shop.example.com/products/123",
"headers": {
"user-agent": "ExampleAgent/1.0 (+https://agent.example.com/bot)",
"signature-agent": "<as sent>",
"signature-input": "<as sent>",
"signature": "<as sent>"
}
}
}
}- Free, no key. Send the headers byte for byte as your agent sent them; re-typing a header breaks the signature.
- allowed_signature_agents takes a host name for a signer that publishes at the well-known path, or the exact https URL of a JWKS or Signature Agent Card.
- A deny lists reasons (for example an expired signature, a directory that didn't return 200, or a signature made for another domain), so you can fix the signer before sites see it.
- Allow answers can be cached for at most 5 minutes.
Rightshold verifies a signed request you send it against a policy and returns allow or deny with reasons. It doesn't generate or store your private key, sign requests for you, register your agent with Cloudflare or anyone else, or guarantee any site will let your agent in. Passing gate_check doesn't mean Cloudflare's verifier accepts the same request; test each one. Not legal advice.
What are the sources?
- Cloudflare docs: Web Bot Auth (key generation, signed directory, registration, Signature-Agent format, crawltest endpoint, limitations)
- Cloudflare docs: Verified bots (signed agents Verified since July 1, 2026; Direct and Intermediary)
- IETF: draft-ietf-webbotauth-httpsig-protocol-01, HTTP Message Signatures for automated traffic (Oct 29, 2026)
- IETF: draft-singh-webbotauth-hosted-directories-00, Hosted Key Directories for Web Bot Auth
- RFC 9421: HTTP Message Signatures
- RFC 7638: JSON Web Key (JWK) Thumbprint
- RFC 8037: CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JOSE (OKP keys, Ed25519)
- The Gate on Rightshold
Related
- How to verify which AI agent is hitting your site with Web Bot Auth
- How to let a headless-browser AI agent complete checkout without turning off bot protection
- How to require a verified agent credential before an API returns paid or sensitive data
- How to block spoofed ChatGPT-User and Perplexity-User requests at the edge without blocking the real ones
Not legal advice. JSON · Markdown · All answers
Get a free API key in one call
No card and no email needed. 100 free credits; the license and policy checks stay free without a key.
curl -s -X POST https://www.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_... # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
-H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'
