| Internet-Draft | HTTP Message Signatures for Bots | August 2026 |
| Meunier & Major | Expires 6 February 2027 | [Page] |
This document describes a protocol for identifying automated traffic using [HTTP-MESSAGE-SIGNATURES]. The goal is to allow automated HTTP clients to cryptographically sign outbound requests, allowing HTTP servers to verify their identity with confidence.¶
It defines the Signature-Agent header field for in-band key discovery, a
key directory format based on JWKS, and a well-known URI at which that
directory is served.¶
This note is to be removed before publishing as an RFC.¶
The latest revision of this draft can be found at https://thibmeu.github.io/http-message-signatures-directory/draft-meunier-webbotauth-httpsig-protocol.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-meunier-webbotauth-httpsig-protocol/.¶
Discussion of this document takes place on the Web Bot Auth Working Group mailing list (mailto:web-bot-auth@ietf.org), which is archived at https://mailarchive.ietf.org/arch/browse/web-bot-auth/. Subscribe at https://www.ietf.org/mailman/listinfo/web-bot-auth/.¶
Source for this draft and an issue tracker can be found at https://github.com/thibmeu/http-message-signatures-directory.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 6 February 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Agents are increasingly used in business and user workflows, including AI assistants, search indexing, content aggregation, and automated testing. These agents need to reliably identify themselves to origins for several reasons:¶
Regulatory compliance requiring transparency of automated systems¶
Origin resource management and access control¶
Protection against impersonation¶
Service level differentiation between human and automated traffic¶
Current identification methods such as IP allowlisting, User-Agent strings, or shared API keys have significant limitations in security, scalability, and manageability. This document defines a protocol enabling agents to cryptographically identify themselves using [HTTP-MESSAGE-SIGNATURES]. It proposes that every request from bots be signed by a private key owned by its provider. This way, every origin can validate the service identifier. Section 4 defines what that identifier is and what validation it establishes.¶
There is an increase in agent traffic on the Internet. Many agents choose to identify their traffic today via IP Address lists and/or unique User-Agents. This is often done to demonstrate trust and safety claims, support allowlisting/denylisting the traffic in a granular manor, and enable sites to monitor and rate limit per agent operator. However, these mechanisms have drawbacks:¶
User-Agent, when used alone, can be spoofed meaning anyone may attempt to act as that agent. It is also overloaded - an agent may be using Chromium and wish to present itself as such to ensure rendering works, yet it still wants to differentiate its traffic to the site.¶
IP blocks alone can present a confusing story. IPs on cloud plaforms have layers of ownership - the platform owns the IP and registers it in their published IP blocks, only to be re-published by the agent with little to bind the publication to the actual service provider that may be renting infra. Purchasing dedicated IP blocks is expensive, time consuming, and requires significant specialist knowledge to set up. These IP blocks may have prior reputation history that needs to be carefully inspected and managed before purchase and use.¶
An agent may go to every website on the Internet and share a secret with them like a Bearer from [OAUTH-BEARER]. This is impractical to scale for any agent beyond select partnerships, and insecure, as key rotation is challenging and becomes less secure as the consumers scale.¶
Using well-established cryptography, we can instead define a simple and secure mechanism that empowers small and large agents to share their identity.¶
This protocol has two objectives:¶
Continuity of bot trust, so that an origin can tell it is dealing with the same party it dealt with before.¶
Optional binding to another anchor, such as a domain.¶
It works under two constraints:¶
Preserve the simplicity of usage for bots, and the simplicity of action for websites.¶
Require no pre-established relationship between the two.¶
The second constraint is what rules out shared secrets and per-site onboarding. The first is a statement about operational cost on both ends: a site today greps its logs for an IP address and a User-Agent, and with this protocol it greps for a handle it can verify.¶
This protocol operates solely at the HTTP layer. It allows signatures to be generated and verified without modifying the transport layer or TLS stack. It enables flexible deployment across proxies, gateways, and origin servers, and aligns with existing tooling and infrastructure that already inspect and manipulate HTTP headers.¶
Because the signature is embedded in the request itself, it travels with the message through intermediaries, preserving end-to-end verifiability even when requests are forwarded or transformed within the HTTP layer.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
The following terms are used throughout this document:¶
An entity initiating requests through an agent. May be a human operator or another system.¶
An orchestrated user agent (e.g. Chromium, CURL). It implements the HTTP protocol and constructs valid HTTP requests with [HTTP-MESSAGE-SIGNATURES] signatures.¶
An HTTP server receiving signed requests that implements the HTTP protocol and verifies [HTTP-MESSAGE-SIGNATURES] signatures. It acts as a verifier of the signature as defined by [HTTP-MESSAGE-SIGNATURES].¶
This section defines the identifiers produced by this protocol and what a verifier can conclude from a valid signature.¶
An Agent identifies itself with the HTTPS URL it publishes its keys at, carried
in Signature-Agent (Section 5.2.1). A verifier resolves that member
value (Section 5.5) and checks the signature against the
keys it returns. The identifier is the URL the verifier fetched, which for a
directory member is the well-known URI rather than the value the client sent.
What the verifier ends up with is a pair: that URL, and a key the URL provides.
Origins can log, rate limit, allowlist, or block the URL the way they do IP
addresses and User-Agent today.¶
The URL on its own carries nothing. A client picks the value it sends, so an
unresolved Signature-Agent is a claim rather than an identity. It becomes an
identifier once the verifier fetches it and finds that it provides a key that
verifies the request (Section 4.4). Until then, verifiers MUST NOT attach policy to it.¶
A valid signature over a resolved URL proves that the request came from a holder of a key that URL publishes, and that requests with the same URL come from holders of keys that URL publishes. Section 5.2.3 bounds reuse. It says nothing about who operates the Agent, whether the Agent is benign, or whether the request is authorized. Those are origin policy.¶
Nothing stops an Agent from abandoning a URL and standing up another one, and the protocol does not try to prevent this. It targets honest clients that want to be recognised across requests.¶
Because the identifier is the URL and not the key, an Agent can rotate keys without losing continuity. It publishes the new key alongside the old one, then drops the old one (Section 5.5.2). The URL does not change, so a verifier that recognised it before still recognises it after. No name and no third party are involved.¶
keyid selects which key verifies a given request. Verifiers cannot use it to
carry continuity across a rotation, as that value is derived from the key
material.¶
[SIGNATURE-KEY] takes a different approach, where a long-lived key signs short-lived delegated keys. Deployments MAY use it. This document does not define rotation that way.¶
Signature-Agent is RECOMMENDED but not required. Without it, a verifier has
only the key, and the identifier is the keyid thumbprint defined in
Section 5.2. Verification still works, provided the
verifier already holds that key.¶
This mode has no rotation. A new key is a new identifier, and the verifier has no way to connect the two.¶
Resolving a Signature-Agent URL over TLS establishes that the host named in
the URL served this key set at fetch time.
Whoever controls that URL says this key signs for it. That
is what makes the URL usable as an identifier, and all it gives you. It does
not say that the operator of that URL is honest, or that is is the same party
everyone knows about.¶
What matters is the association between a URL and the keys published there. A
verifier that already holds the keys does not need to fetch it. A verifier MUST NOT attribute a request to a Signature-Agent URL unless it made this
ssociation. This can be either by resolving the URL itself, at request time or
ahead of it, or from Section 5.5.3. Verifiers may refetch a URL
to handle up key additions and removals, bounded by Appendix C.4.¶
Where a verifier obtains the same pair from more than one source, the newer
pair wins, including when it omits a key that older resolution included.
Pairs are ordered by when they were produced, not when the verifier obtained
them: the created parameter for a directory response signature
(Appendix B), and the time of the fetch for a directory the
verifier resolved itself.¶
A well-known URL is a special case of the above. When a
Signature-Agent value resolves through the directory type
(Section 5.5), the identifier is still the URL, but that
URL now names a domain rather than an arbitrary path on one. [WELLKNOWN-URI]
reserves the path, so the domain operator stands behind the key set.¶
In practice, this is meant to allow additional information to be carried against a name. That mechanism lives in Appendix B. A verifier that wants to use this case may also recognise the shape of the URL and apply those checks itself.¶
This protocol does not authenticate human users, does not provide anonymous authentication, and does not define authorization or delegation. It does not define how trust is accrued, held, or exchanged, and it defines no mechanism for one origin to convey an opinion about an Agent to another. See Section 7.¶
A client has a choice whether to sign its requests, and an origin has a choice how it treats signed and unsigned requests. Multiple factors could influence either decision, but the decisions themselves are outside the scope of this document.¶
A User initiates an action requiring the Agent to perform an HTTP request.
The Agent constructs the request, generates a signature using its signing key,
and includes it in the request as defined in Section 3.1 of [HTTP-MESSAGE-SIGNATURES]
along with the Signature-Agent header for discovery of its verification key.
Upon receiving the request, the Origin ensures it has the verification key for the Agent,
validates the signature, and processes the request if the signature is valid.¶
Signature verification can be performed either directly by origins or delegated to a fronting proxy. Direct verification by origins provides simplicity and control. Proxy verification offloads processing and enables shared caching across multiple origins. The choice depends on traffic volume and operational requirements.¶
[HTTP-MESSAGE-SIGNATURES] defines components to be signed.¶
Agents MUST include at least one of the following components:¶
@authorityas defined in Section 2.2.3 of [HTTP-MESSAGE-SIGNATURES]¶
@target-urias defined in Section 2.2.2 of [HTTP-MESSAGE-SIGNATURES]¶
Agents MUST include the following @signature-params as defined in Section 2.3 of [HTTP-MESSAGE-SIGNATURES]¶
createdas defined in Section 2.3 of [HTTP-MESSAGE-SIGNATURES]¶
expiresas defined in Section 2.3 of [HTTP-MESSAGE-SIGNATURES]¶
keyidMUST be a base64url JWK SHA-256 Thumbprint as defined in Section 3.2 of [JWK-THUMBPRINT] for RSA and EC, and in Appendix A.3 of [JWK-OKP] for ed25519.¶
tagMUST be web-bot-auth¶
The signing key is available to the agent at request time. Algorithms should be registered with IANA as part of HTTP Message Signatures Algorithm registry.¶
The creation of the signature is defined in Section 3.1 of [HTTP-MESSAGE-SIGNATURES].¶
It is RECOMMENDED that expiry be no more than 24 hours.¶
The components above bind the signature to an authority, not to a request. A
signature covering @authority alone verifies against any method, path, or body
sent to that authority until it expires, so anyone who observes one request can
reuse it against the same origin until then.
expires bounds how long that lasts; the covered components bound what it
reaches. Agents that want to narrow it SHOULD also cover @method, and
either @path or @target-uri, as Appendix D.2 does. A signer that omits them remains conformant.
Appendix C.7 covers what that costs on the wire.¶
No component covers the body. An Agent that needs one MUST send and cover
Content-Digest [DIGEST-FIELDS]. This document does not require it. Most
automated traffic is GET, and a mandatory digest would force every Agent to
buffer request bodies it would otherwise stream.¶
Signature-Agent is a Dictionary Structured Header as defined in
Section 3.2 of [STRUCTURED-HEADERS]. Its member values MUST be String Items
that contain a [URI], whose scheme MUST be https. If dictionary values are
not valid URI-references, the entire header field MAY be ignored.¶
Each member carries a type parameter, a Token Item as defined in
Section 3.3.4 of [STRUCTURED-HEADERS], naming the discovery mechanism that
resolves the value to key material. Section 5.5 defines
the types. When type is absent, its value is directory. A verifier that
does not support a type value MUST ignore that member, and MUST NOT infer the
mechanism from the URI path, media type, or response body.¶
Earlier versions of this protocol defined Signature-Agent as a bare String,
and deployments still send it (Appendix E.1.3). A verifier MAY accept that
form and treat it as a dictionary with a single member whose key is the label
of the signature covering it. Signers MUST send the dictionary form. The two
are distinguishable on the wire: a String Item begins with a double quote "
while a Dictionary member key does not.¶
It is RECOMMENDED that the Agent sends requests with Signature-Agent header, as described in Section 5.2.5.
If the header is to be sent, one of its members MUST be signed as a component as defined in Section 2.1 of [HTTP-MESSAGE-SIGNATURES].
The Signature-Agent member identifies where candidate key material can be found.
The key used to verify the signature is selected by the keyid parameter of the
corresponding Signature-Input member.¶
This results in the following components to be signed¶
("@authority" "signature-agent";key="sig1")
¶
It is RECOMMENDED that the key matches the signature label.¶
A request MAY contain more than one Web Bot Auth signature. Each signature is
identified by its HTTP Message Signatures label. When Signature-Agent is
present, each signer SHOULD provide a Signature-Agent member for its label.¶
A signer MAY cover members from another signature label, which preserves
evidence that another signer contributed to the request. A signer that covers
"signature";key=X MUST also cover "signature-input";key=X, and MUST cover
every component identifier listed in "signature-input";key=X.¶
A signature value on its own does not identify the message it was computed
over, which is why Section 7.3.7 of [HTTP-MESSAGE-SIGNATURES] recommends
against signing one. Covering signature-input is not sufficient:
it lists component identifiers, whose values resolve against whatever
message the verifier holds. An outer signature that named those identifiers
without covering them would still verify after the whole header set was lifted
onto a different message, the ambiguity
Section 7.3.7 of [HTTP-MESSAGE-SIGNATURES] describes. Covering the union
addresses this: the outer signer commits to a message on which the inner signature
is checkable, under which key, and over which validity window.¶
A signer that cannot cover one of those components, because it changed the
value the inner signature was computed over, MUST NOT cover the inner
signature member. It signs the request on its own terms, and the inner
signature is left untouched.¶
Verifiers MUST validate each signature independently against its own covered components and its own key. An outer signature that covers an inner one is evidence that those bytes, over that set of components, were present. It does not make the inner signature valid, and it does not express authorization, delegation, or consent. Those meanings are deployment policy, or are carried in separately signed fields.¶
Origins MAY want to prevent signatures from being spoofed or used multiple times by bad actors and thus require a nonce to be added to the @signature-params.
This is described in Section 7.2.2 of [HTTP-MESSAGE-SIGNATURES].¶
Agents SHOULD extend @signature-parameters defined in Section 5.2 as follows:¶
noncebase64url encoded random byte array. It is RECOMMENDED to use a 64-byte array.¶
Client MUST ensure that this nonce is unique for the validity window of the signature, as defined by created and expires attributes.¶
Agents MAY include additional components, such as specific HTTP headers, in the signature. This can be prompted by the origin requesting additional headers, as described in Section 5.3, or initiated by the agent to provide more information within the signature scope. For example, an agent might include an HTTP header expressing its intent and sign it.¶
Origins MAY ignore certain headers at their own discretion, and request a new signature, as described in Section 5.3.¶
An Agent SHOULD send a request with the signature generated above. Updating the overview diagram, the flow looks as follow.¶
The Agent SHOULD send requests with two headers¶
Signature defined in Section 5.2¶
Signature-Input defined in Section 5.2¶
As described in Section 5.2.1, it is RECOMMENDED that the Agent also send
the Signature-Agent header. Without it the Agent is identified by its key
alone, with the consequences described in Section 4.3.¶
Section 5 of [HTTP-MESSAGE-SIGNATURES] defines the Accept-Signature field which can be used to request a Message Signature from a client by an origin.
An Origin MAY choose to request signatures from clients that did not initially provide them. If requesting, Origins MUST use the same parameters as those defined by the Section 5.2.
The status code SHOULD be 403 Forbidden as defined in Section 15.5.4 of [HTTP].¶
Origin MAY request a new signature with tag "web-bot-auth" even if a nonce is provided, for example if it believes the nonce is a replay, or if it doesn't store nonces and thus requests new signatures every time. The status code SHOULD be 429 Too Many Requests as defined in Section 4 of [HTTP-MORE-STATUS-CODE].¶
Upon receiving an HTTP request, the origin has to verify the signature. The algorithm is provided in Section 3.2 of [HTTP-MESSAGE-SIGNATURES]. Similar to a regular User-Agent check, this happens at the HTTP layer, once headers are received.¶
Additional requirements are placed on this validation:¶
During step 1 to 3 included, if the Origin fails to parse the provided Signature, Signature-Input, or Signature-Agent headers, it MAY respond with status code 400 Bad Request as defined in Section 15.5.1 of [HTTP].¶
During step 4, the Origin MAY discard signatures for which the tag is not set to web-bot-auth.¶
During step 5, the Origin MAY discard signatures for which it does not know the keyid for the Signature-Agent URL the signature covers.¶
During step 5, if the keyid is not known for that URL, the Origin MAY fetch key material as indicated by the Signature-Agent header defined in Section 5.2.1. Fetching key material affects only whether verification is possible, not what a valid signature means (Section 4.4).¶
Key lookup MUST be keyed on the (URL, key) pair, not on the key alone. A
verifier that indexes by keyid alone will verify a request as coming from one URL
that provides a key it learned from another, and attribute that request to the URL the client
asserted. The party whose URL is asserted cannot detect or stop this: its own
directory is never fetched, so no rotation or removal has any effect.¶
Origin MAY require the nonce to satisfy certain constraints: be globally unique using a global nonce store, be unique to a specific location or time window using a local cache, or no constraint at all.¶
This section describes how a verifier resolves a Signature-Agent URL to key
material. Section 4.4 covers what the fetch does and does not
establish.¶
The reference for discovery is an HTTPS URL, carried in a Signature-Agent
member as defined in Section 5.2.1. The member's type parameter names
how the URL resolves to key material. This protocol defines three types:¶
directoryThe member value MUST be the ASCII serialization of an origin as defined in
Section 6.2 of [ORIGIN], and a verifier MUST ignore a member carrying
anything else (an empty path / MAY be accepted though).
Resolve the HTTP Message Signatures Directory at the well-known
URI registered in Section 8.1, at that origin. This is the default when no
type parameter is present.¶
jwks_uriResolve the member value as a direct JWK Set URI.¶
cimdResolve the member value as a Client ID Metadata Document [CIMD] URI. The
document then provides key material through jwks or jwks_uri.¶
All three types produce an identifier: the URL the verifier resolved, with any
query and fragment discarded. For directory that is the well-known URI, one
per origin. For jwks_uri and cimd it is the member value; the verifier
fetches that value as sent, so the query is dropped from the identifier and not
from the request. Otherwise one key set would yield an identifier per spelling,
and an Agent could mint them at will.¶
Identifiers are compared after normalization as described in Section 6.2.2 of [URI] and Section 6.2.3 of [URI]. Two identifiers are the same when their normalized forms are equal octet for octet.¶
The types differ in what additional information the verifier learns from the URL.
TLS authenticates the host but not the path, and nothing reserves the jwks_uri or cimd path to the host's operator. The well-known URI is reserved, so directory
additionally names a domain (Section 4.5).¶
For all types, the key is selected using the keyid parameter in
Signature-Input.¶
Note: when a JWK set is served at the well-known URI registered in
Section 8.1, JWK MAY carry a kid. In this case, it MUST be set to the
thumbprint defined in Section 5.2, so a verifier
selects a key by matching keyid against kid. Deriving kid from the key
material keeps it globally unique and lets a verifier check the directory's own
labelling rather than trusting it.¶
jwks_uri and cimd resolve to key sets that may serve other consumers, where
kid is an operator-chosen label. A verifier that cannot match keyid against
kid there computes thumbprints instead.¶
Signature-Agent: sig1="https://signature-agent.test" Signature-Agent: sig1="https://signature-agent.test/jwks.json";type=jwks_uri Signature-Agent: sig1="https://signature-agent.test/card";type=cimd¶
All three types resolve to a JSON Web Key Set (JWKS) as defined in
Section 5 of [JWK]. The alg parameter is restricted to algorithms
registered in the HTTP Signature Algorithms section of
[HTTP-MESSAGE-SIGNATURES-IANA].¶
The directory MUST be served over HTTPS. A directory served at the well-known
URI registered in Section 8.1 MUST be served with media type
application/http-message-signatures-directory+json.¶
A verifier SHOULD validate the directory format and reject malformed entries.¶
GET /.well-known/http-message-signatures-directory HTTP/1.1
Host: example.com
Accept: application/http-message-signatures-directory+json
HTTP/1.1 200 OK
Content-Type: application/http-message-signatures-directory+json
Cache-Control: max-age=86400
{
"keys": [{
"kty": "OKP",
"crv": "Ed25519",
"kid": "NFcWBst6DXG-N35nHdzMrioWntdzNZghQSkjHNMMSjw",
"x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs",
"use": "sig",
"nbf": 1712793600,
"exp": 1715385600
}]
}
¶
Directory operators SHOULD rotate keys by publishing the old and the new key together, then removing the old one:¶
Add the new key to the directory before its intended use date¶
Continue to include the old key until its expiration date¶
Remove expired keys from the directory¶
Removing a key from the directory deactivates it. Verifiers stop accepting it once their cached copy expires, so the directory's cache lifetime bounds how long a removed key keeps verifying. Verifiers SHOULD cache the directory contents and refresh upon expiration, as described in Appendix C.4.¶
It is not a revocation mechanism, and this document does not define any.¶
IP addresses and user-agent have been aggregated and distributed via lists. This section says what a verifier may conclude from key material it did not fetch itself. Defining a format for redistribution is out of scope.¶
A verifier MUST NOT attribute a request to a Signature-Agent URL on the basis
of redistributed key material unless it carries, for the key in question, a
valid directory response signature as described in Appendix B
whose expires has not passed. Without that proof the material stays usable
for verifying signatures, but it carries no URL, so the identifier falls back
to the key thumbprint (Section 4.3).¶
The main requirement is to terminate the TLS connection. A verifier polling a directory on its own schedule is resolving it. So is a control plane polling on behalf of the verifiers it serves. None of these options constitute redistribution. Nor is a list that names directory URLs rather than embedding keys. For instance, [REGISTRY] works that way, and the verifier still resolves them.¶
[SIGNATURE-KEY] defines a separate key discovery header for HTTP Message
Signatures. Deployments MAY use it when they need that model. This protocol
uses Signature-Agent as its default discovery mechanism.¶
Per-request signing and verification costs CPU; uncached key discovery adds latency. For high request rates, an origin can verify a request-specific signature once and issue a session credential for later requests. This can amortize asymmetric verification and reduce bytes, but adds the risks of token theft and replay.¶
A reused signature already has token semantics until expires. A session
established from one extends that window past expires unless the credential is
bounded to it: no longer-lived, and no wider in scope than the components the
signature covered. Session establishment and binding are out of scope.¶
We reassess Section 7.1.2 of [HTTP-MESSAGE-SIGNATURES]. Clients SHOULD use TLS [RFC8446] (https) or equivalent transport security when making requests with Message signatures. Failing to do so exposes the Message signature to numerous attacks that could give attackers unintended access.¶
This include reverse proxy and their consideration presented in Section 6.7.¶
An origin SHOULD refuse Signature headers when communicated over an unsecured channel.¶
Origins should account for the overhead of signature verification in their operations. A local cache of public keys reduces network requests and verification latency. The choice of signing algorithm impacts CPU requirements. Origins should monitor verification latency and set appropriate timeouts to maintain service levels under load. See Section 5.6: a session amortizes that cost by replacing verification with a bearer credential. Appendix C.7 covers the byte cost.¶
Clients control the nonce. While Section 5.2.3 mandates that clients MUST provide a globally unique nonce, it is the origin's responsibility to enforce it.¶
Different validation policies have different performance and operational considerations. Global uniqueness requires a global nonce store. Some origins may find that their use case can tolerate sharding on location, timing, or other properties.¶
This document defines no revocation. Removing a compromised key from the directory is the only remedy, and it takes effect at each verifier on its next refresh, so the key can keep verifying for as long as Section 5.5.2 allows. The protocol carries no channel back to verifiers, so an Agent cannot reach them sooner. Signature lifetimes (Section 5.2) are the only lever that acts faster.¶
Agents SHOULD remove a compromised key and publish a replacement immediately. Origins should support rapid key rotation and monitor for suspicious signature patterns.¶
Implementations SHOULD NOT reuse a signing key for different purposes. For example, if an agent implementor has two agents they want to differentiate, these should use distinct signing keys and signing key directories.¶
An origin may be placed behind a reverse proxy, which means the proxy will see
the Signature and Signature-Agent headers before the origin does.
A proxy SHOULD NOT strip the Signature or Signature-Agent headers from
requests.¶
A proxy SHOULD NOT replay signatures against other reverse proxies used by the origin, as this allows impersonation of the principal signature agent.¶
Origins MAY require a specific nonce policy to prevent such malicious behaviour
and decide to validate the signature themselves. This has to be done in
accordance with Section 6.3. For example, an origin could
require a nonce derived from public information (such as the current date),
mandate nonce chaining (where each nonce is the hash of the previous one),
or provide its own nonce in an Accept-Signature response to challenge the agent.¶
Such policies MAY incur additional round-trip between the client and the origin
to convey accept-signature header, or deployment specific exchanges.¶
Section 7.2.5 of [HTTP-MESSAGE-SIGNATURES] allows an intermediary to relabel
a signature, because the label of a Signature dictionary member is not part
of the signature base. The key of a Signature-Agent member is different: when
a signature covers "signature-agent";key="agent2", that key appears in the
signature base, so changing it invalidates the signature. Only the holder of
the signing key can produce a signature over the new member key.¶
An intermediary MUST NOT alter the key of a Signature-Agent member that is
covered by a signature it is not able to recompute. Relabeling the Signature
dictionary member remains permitted.¶
A signer acting as an intermediary on its own signature is not restricted by this, since it can sign the result.¶
As described in Section 5.5, verifiers may fetch key directories based on
the value conveyed in Signature-Agent when included in a request. Since
clients control the Signature-Agent header value, this introduces a risk of
server-side request forgery (SSRF) attacks by malicious clients.¶
Verifiers SHOULD take appropriate precautions as follows:¶
Response sizea directory can be arbitrarily large. Verifiers SHOULD reject responses exceeding a defined byte limit after content decoding.¶
Key counta JWKS with many keys forces O(n) key search. Verifiers SHOULD enforce a maximum key count.¶
Fetch latencyno timeout allows slowloris-style exhaustion. Verifiers SHOULD apply a wall-clock timeout to directory fetches.¶
Redirect chainsunbounded HTTP redirects can be used to amplify requests. Verifiers SHOULD limit redirect depth.¶
Network address rangesno address filtering can target internal services. Verifiers SHOULD prevent directory fetches to private, loopback, and link-local address ranges.¶
Further recommendations can be found in the Open Worldwide Application Security Project (OWASP) SSRF Prevention Cheat Sheet [OWASP-SSRF].¶
Test keys, including the example keys in [HTTP-MESSAGE-SIGNATURES], MUST NOT be used in production. Verifiers SHOULD reject known test keys when they are detected in key directories or out-of-band configuration.¶
Deployments MUST NOT treat a precomputed Web Bot Auth signature as a long-lived access credential. A reusable static signature has bearer-token semantics and can be replayed until the covered signature parameters, key, or verifier policy make it unusable.¶
Agents SHOULD generate signatures for the request being sent, with bounded
created and expires values. Long expiration windows increase replay risk.¶
Resolving a Signature-Agent URL can fail in several ways: the name does not
resolve, the connection or TLS handshake fails, the response is not a directory
or contains no key matching keyid, or the fetch is refused by the verifier's
own limits (Section 6.8). All have the same outcome for the request in hand. The
verifier holds no association between that URL and the signing key, so under
Section 4.4 it MUST NOT attribute the request to that URL. It may
still verify the signature if it holds the key by other means, in which case the
identifier is the thumbprint (Section 4.3); otherwise the request is unverified.¶
They differ in what they say about cached state, and verifiers MUST keep them apart. A directory that resolves and does not contain the key is evidence: it is newer than whatever the verifier holds, and under Section 4.4 replaces it. That is how a removed key stops verifying. A directory that fails to resolve is not evidence and MUST NOT evict a cached entry, or an operator's outage revokes its keys at every verifier at once.¶
A failed fetch says nothing about the signer. It does not prove the signer is malicious, and it does not make the request trusted. What an origin does with an unverified request is local policy, and treating it as a distinct outcome rather than as success or failure is discussed in Appendix C.1. Verifiers should also expect failures to be correlated: a single operator's directory going down takes out every request naming it at once, across every verifier whose cache expires in the same window.¶
Most HTTP requests carry no signature. A verifier that sees none has learned nothing about the sender: not that it is automated, not that it is human, not that it is evading anything. Absence of a signal is not evidence about the party that did not send it, in the same way that a failed fetch (Section 6.11) is not evidence about the signer.¶
What an origin does with a request it cannot attribute is its own decision, as it was before this protocol existed. This document neither requires an origin to treat unsigned requests differently nor gives it grounds to.¶
This protocol assumes that automated clients identify themselves explicitly using digital signatures. The identity associated with a signing key is expected to be publicly discoverable for verification purposes. This reduces anonymity and allows receivers to associate requests with specific agents. If an agent wishes not to identify itself, this is not the right choice of protocol for it.¶
The key used for signing MUST NOT be tied to a specific human individual. Keys SHOULD represent a role, company, or automation identity (e.g., "news-aggregator- bot", "example-crawler-v1"). This avoids accidental exposure of personally identifiable information and prevents the misuse of keys for user tracking or profiling.¶
To limit tracking risks, implementations SHOULD avoid long-lived, globally unique key identifiers unless strictly necessary. Key rotation SHOULD be supported, and clients SHOULD take care to avoid signing information that could be used to correlate activity across contexts, especially where sensitive user data is involved.¶
A key directory should only contain keys actively used for signing. Additional keys or metadata expose more about the signing service than verification requires. Verifiers fetching a directory also reveal something about their verification patterns, so directory servers should avoid logging personally identifiable information from directory requests.¶
This section contains considerations for IANA.¶
This document updates the "Well-Known URIs" Registry [WellKnownURIs] with the following values.¶
| URI Suffix | Change Controller | Reference | Status | Related information |
|---|---|---|---|---|
| http-message-signatures-directory | IETF | this document | permanent | None |
The following entries should be added to the IANA "media types" registry:¶
"application/http-message-signatures-directory+json"¶
The templates for these entries are listed below and the reference should be this RFC.¶
application¶
http-message-signatures-directory¶
N/A¶
N/A¶
"binary"¶
N/A¶
this specification¶
Services that implement the signer role for HTTP Message Signatures and verifiers that interact with the signer for the purpose of validating signatures.¶
N/A¶
see Authors' Addresses section¶
COMMON¶
N/A¶
see Authors' Addresses section¶
IETF¶
[USE-CASES] collects the use cases this group has discussed. Most are served by the URL alone. The table below records which ones need the domain binding in Appendix B, and why.¶
| Use case | What the origin does | Needs |
|---|---|---|
| Mitigating volumetric abuse | Rate limit per URL | URL |
| Controlling access by bots | Set policy per URL | URL |
| Providing different content to bots | Recognise a given URL | URL |
| Auditing bot behaviour | Group logs by URL | URL |
| Classifying traffic | Correlate observed behaviour with a URL | URL |
| IP address mobility and sharing | Nothing: the signature does not depend on the IP | URL |
| Robots.txt alignment | Match the crawler against a name in the file | Domain |
| Conveying contextual information | Read signed headers alongside the identifier | Domain |
The last two are the pattern from Section 4.5. Both consume something held against a name rather than against the key: a robots.txt file names crawlers, and contextual assertions are only worth as much as the party making them. End-user authentication and anonymous authentication are out of scope.¶
This appendix describes what a verifier checks when it wants the domain a key
is published under, rather than the URL on its own. It applies to the
directory type in Section 5.5. Verification,
rotation, and continuity do not depend on any of it, and a verifier that only
needs the URL as an identifier can skip the whole appendix.¶
Authority over the domain comes from the TLS connection to the directory. Nothing below adds to that.¶
It is RECOMMENDED that a directory server construct and include one HTTP
Message Signature per key with the response, as defined in
[HTTP-MESSAGE-SIGNATURES]. Each key SHOULD be used to provide one signature.
These signatures prove possession of the advertised keys and, by covering
@authority, prevent the key set from being re-served under a different
authority. This matters for a domain-bound identifier, where the verifier is
about to consume information it holds against the name: it distinguishes a key set
the key holders assembled from one that was copied.¶
Directory server MUST include the following covered components:¶
@authorityas defined in Section 2.2.3 of [HTTP-MESSAGE-SIGNATURES]. req flag defined in Section 2.4 of [HTTP-MESSAGE-SIGNATURES] MUST be set.¶
content-digestas defined in [DIGEST-FIELDS].¶
Directory server MUST include the following @signature-params as defined in
Section 2.3 of [HTTP-MESSAGE-SIGNATURES]¶
createdas defined in Section 2.3 of [HTTP-MESSAGE-SIGNATURES]¶
expiresas defined in Section 2.3 of [HTTP-MESSAGE-SIGNATURES]¶
Without them the signature is a permanent assertion that these keys were bound to this authority at some unstated time, of no use to a verifier consuming it through Section 5.5.3.¶
keyidMUST be a base64url JWK SHA-256 Thumbprint as defined in Section 3.2 of [JWK-THUMBPRINT] for RSA and EC, and in Appendix A.3 of [JWK-OKP] for ed25519.¶
tagMUST be http-message-signatures-directory¶
A verifier relying on the domain MUST validate these signatures using the keys
provided by the directory, MUST validate the Content-Digest field against the
response body, and MUST ignore keys that do not have a corresponding valid
signature. A verifier MUST reject a directory response signature whose
created is in the future, as it would a certificate that is not yet valid.
Section 4.4 orders competing evidence by created, so a
future-dated signature would outrank every later fetch.¶
The binding is not exclusive. Several domains may publish the same key, and the binding attaches to the pair the verifier validated, not to the key on its own. A verifier that recognises a key under one domain has learned nothing about the same key served under another.¶
This appendix is operational guidance. It does not define new protocol requirements.¶
Verifiers should keep three outcomes distinct:¶
verifiedthe signature and key material validate.¶
invalidthe signature, covered components, key, or freshness checks fail.¶
unverifiedthe verifier cannot obtain enough information to decide, for example because directory discovery failed or the key is unknown.¶
Origins can apply local policy to each outcome. During deployment, treating
unverified as one bot-management signal is safer than treating it as either
verified or invalid.¶
Directory resources are bootstrap material. Operators serving a directory should make it reachable without requiring Web Bot Auth on the directory request. They should also avoid bot protection rules that block ordinary verifier fetches of the well-known resource.¶
The directory endpoint should support GET. Supporting HEAD, ETag,
Last-Modified, Cache-Control, and conditional requests can reduce fetch
load. Cache is specifically discussed in Appendix C.4.¶
Verifiers fetch directories named by untrusted requests, and should bound those fetches as described in Section 6.8.¶
Verifiers should also coalesce concurrent fetches for the same directory and apply per-directory or per-origin concurrency limits. This avoids a fetch storm when many requests reference the same uncached directory.¶
Verifiers should use normal HTTP caching semantics [HTTP-CACHE] for key
directories. In particular, verifiers should respect Cache-Control, Expires,
Date, ETag, and Last-Modified when present.¶
A verifier should not fetch the directory for every request. It should refresh cached directories when they become stale, and can use background refresh with jitter to avoid synchronized refetches.¶
Verifiers can cache unsuccessful discovery outcomes for a short period to reduce repeated fetches. Negative cache entries should expire after no more than five minutes. They are operational throttling state, not proof that a signature is invalid.¶
Network failures, TLS failures, and 5xx responses should be treated as
transient unless local policy says otherwise. Verifiers should retry with bounded
exponential backoff and jitter. When a directory response includes
Retry-After, verifiers should respect it as described by [HTTP] and
[HTTP-BEST-PRACTICES].¶
Shorter signature lifetimes reduce replay risk but increase sensitivity to clock skew and signing failures. Nonces provide stronger replay defense, but require state at the verifier. Some deployments can tolerate bounded replay for short windows; others need strict Section 6.3.¶
These choices are deployment policy. Verifiers should avoid accepting signatures with freshness windows longer than their risk model permits.¶
Covering per-request components costs bytes when a connection is reused. HPACK
[HPACK] and QPACK [QPACK] can index a repeated Signature,
Signature-Input, or Signature-Agent value, so a signature reused across
requests on one connection is sent once and referenced afterwards. A per-request
value cannot be referenced; it is sent as a literal every time. Huffman coding
and an indexed field name reduce that literal, they do not replace the
reference.¶
This is not a reason to widen the covered components. The bytes saved are the
bytes of a credential anyone who observes it can replay until expires
(Section 5.2), and one static signature for many
requests is an anti-pattern (Appendix C.12). An encoder that
treats a signature as a credential may also decline to index it
(Section 7.1.3 of [HPACK]).¶
Where the key set is redistributed, revocation latency is already floored by
how often the redistributor republishes, so a short expires on a directory
response signature (Appendix B) buys nothing and costs
availability: at expiry every consumer drops that operator's keys to unverified
at once, with no serving stale. Operators should set expires well beyond the
republication interval of any list they expect to appear in. The lever for
faster revocation is publishing more often, not signing shorter.¶
Web Bot Auth deployments will coexist with existing bot identification signals during rollout. Verifiers can continue to use existing methods such as IP-based checks, forward-confirmed reverse DNS, local allowlists, and reputation systems.¶
Fallback should not turn an unsupported or unverifiable Web Bot Auth signature into a trusted identity. It should leave the request in the origin's existing bot-management path.¶
Proxies and intermediaries need to preserve the fields covered by a signature if the origin will verify that signature. If a proxy rewrites the authority, path, or signed header fields, the origin may no longer see the message that was signed.¶
A deployment can instead verify at the proxy and pass the result to the origin through a deployment-local trusted channel. That assertion is local policy; it is not a replacement for the original HTTP Message Signature.¶
Key directories contain public key material. If browser-based verifiers need to
fetch them cross-origin, a directory server can use a permissive CORS policy such
as Access-Control-Allow-Origin: * without credentials. CORS is not key
authentication and does not replace signature validation.¶
Deployments should avoid:¶
Delegation and chaining are out of scope for this document and are expected to be specified separately. Input is welcome on the associated GitHub issue.¶
This example shows Alice's agent using a remote browser to fetch a resource. The agent signs selected request fields. The remote browser signs the request it sends to the origin and also covers the agent's signature fields. The signature values are illustrative; this is not a test vector.¶
NOTE: '\' line wrapping per RFC 8792
GET /resource HTTP/1.1
Host: origin.example
Signature-Agent: agent="https://agent.alice.example",\
browser="https://browser.example"
Signature-Input: agent=("@method" "@authority" "@path"\
"signature-agent";key="agent");created=1735689600\
;keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"\
;tag="web-bot-auth",\
browser=("@method" "@authority" "@path"\
"signature-agent";key="browser"\
"signature-agent";key="agent"\
"signature-input";key="agent"\
"signature";key="agent");created=1735689601\
;keyid="oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA"\
;tag="web-bot-auth"
Signature: agent=:YWdlbnQtc2lnbmF0dXJl:,\
browser=:YnJvd3Nlci1zaWduYXR1cmU=:
¶
The origin verifies each signature on its own. The agent signature covers the
fields selected by Alice's agent. The browser signature covers the request
sent by the remote browser, its own Signature-Agent member, and all three of
the agent label's fields, as Section 5.2.2 requires. This records
that the remote browser forwarded a request carrying the agent's signature. It
does not say that Alice's agent authorized the remote browser to act for it.¶
These vectors exercise the minimum this document requires, so most of them
cover @authority and nothing else, with an expires far enough out that they
do not age. That combination is a parsing and verification exercise, not a
configuration to copy: as Section 5.2 explains, a
signature covering @authority alone is reusable against that authority for
any method, path, and body until it expires. Deployments should cover more and
expire sooner.¶
The test vectors in this section use the RSA-PSS key defined in Appendix B.1.2 of [HTTP-MESSAGE-SIGNATURES]. This section includes non-normative test vectors that may be used as test cases to validate implementation correctness.¶
This example presents a minimal signature using the rsa-pss-sha512 algorithm over test-request. The request does not contain
a Signature-Agent header.¶
The corresponding signature base is:¶
NOTE: '\' line wrapping per RFC 8792
"@authority": example.com
"@signature-params": ("@authority")\
;created=1735689600\
;keyid="oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA"\
;alg="rsa-pss-sha512"\
;expires=4889289600\
;nonce="JojDFWJ90jf+gZhdKeTyJYsu1XvNPZSFAGhvYq5SuV3gneOEUAhq+xl792WGuD1W+Dr6NRmx+m+t06NsYnL4iA=="\
;tag="web-bot-auth"
¶
This results in the following Signature-Input and Signature header fields being added to the message under the label sig1:¶
NOTE: '\' line wrapping per RFC 8792
Signature-Input: sig1=("@authority")\
;created=1735689600\
;keyid="oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA"\
;alg="rsa-pss-sha512"\
;expires=4889289600\
;nonce="JojDFWJ90jf+gZhdKeTyJYsu1XvNPZSFAGhvYq5SuV3gneOEUAhq+xl792WGuD1W+Dr6NRmx+m+t06NsYnL4iA=="\
;tag="web-bot-auth"
Signature: sig1=:hWPaj85MWQiRkzU4jnIKvdPQiDfMCPIoxOP8nZveNc3aFQ7r/UmXWCwGNImw588iRvTFey5TR3fVEgnXpcttlyK+u5pN831z9Wlr+IMNfub4uEM3SuO+SKFygJZyLG0pf7OAiRcU4C0gyx1BS/+z9ydQTRzDLr88wCkBBRqwGRrSi8HTwxkqg1jugobh93hcnU6gV8MK1n+VnhRprIgl2RQSO6q5cfbB4OS8C4t/8ndW0lYmP2SWzKZJXnpX5Wrj17PuLqnVW6MO8pJnLAMXNvxUdx32KHeq/cHFrzZazZsua3UOoP+k+niHwoQ8bBWj1Vi4mM1mYJK+fk366cCLsQ==:
¶
This example presents a minimal signature using the rsa-pss-sha512 algorithm over test-request. The request contains
a Signature-Agent header.¶
The corresponding signature base is:¶
NOTE: '\' line wrapping per RFC 8792
"@authority": example.com
"signature-agent";key="agent2": "https://signature-agent.test"
"@signature-params": ("@authority" "signature-agent";key="agent2")\
;created=1735689600\
;keyid="oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA"\
;alg="rsa-pss-sha512"\
;expires=4889289600\
;nonce="wcfPQPh7SzkvrIVvhD00vNk9PkxJNY2NVbYl2PVBB4zmUoluSwE7W6bPtF60QA3k8g06FU7PPCD+J58YofY1zg=="\
;tag="web-bot-auth"
¶
This results in the following Signature-Input and Signature header fields being added to the message under the label sig2:¶
NOTE: '\' line wrapping per RFC 8792
Signature-Agent: agent2="https://signature-agent.test"
Signature-Input: sig2=("@authority" "signature-agent";key="agent2")\
;created=1735689600\
;keyid="oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA"\
;alg="rsa-pss-sha512"\
;expires=4889289600\
;nonce="wcfPQPh7SzkvrIVvhD00vNk9PkxJNY2NVbYl2PVBB4zmUoluSwE7W6bPtF60QA3k8g06FU7PPCD+J58YofY1zg=="\
;tag="web-bot-auth"
Signature: sig2=:gHzpLNeHaHIO19NaJH9YMW5dcVSi2s0wOMBr6p18vcofS106sfC4KBIS0/szPlBBd1vIcyQ88B6CTEWIhRAiVrb9zfX0mx1aG12CSGWcYkSirHeyTxhbuJvXd27ed6skWoy4PjXItq38936ivUQjfdIwXh1aX6HxkAC3vRnEdSNfntkLWeEuIQ5BLIOBGE39fSwg27Qjq6OVWYas/9/aFUr3HA34MXWYdp+//cvlEKDp3kRoLOw9ro0AOr6srHrTeEtxon2afcws1aZVSlPdd2fZSEIGmw9HAHLDCEkFTERu1gH2k/zIEqgy7CAYXI9E5slog0cLg/Vc6+f8gih33g==:
¶
Retained for implementers migrating to the dictionary form (Section 5.2.1). Do not copy it into new deployments.¶
This example presents a minimal signature using the rsa-pss-sha512 algorithm over test-request. The request contains
a Signature-Agent header.¶
The corresponding signature base is:¶
NOTE: '\' line wrapping per RFC 8792
"@authority": example.com
"signature-agent": "https://signature-agent.test"
"@signature-params": ("@authority" "signature-agent")\
;created=1735689600\
;keyid="oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA"\
;alg="rsa-pss-sha512"\
;expires=1735693200\
;nonce="XSHtZVCThSIAksXsH9WBs6AtxtXC0eQGiIcUGSoJstFs8lAWakjhrfwzLhyjtme5iXMZvmFWqDEs6cT3Jf+BbQ=="\
;tag="web-bot-auth"
¶
This results in the following Signature-Input and Signature header fields being added to the message under the label sig2:¶
NOTE: '\' line wrapping per RFC 8792
Signature-Agent: "https://signature-agent.test"
Signature-Input: sig2=("@authority" "signature-agent")\
;created=1735689600\
;keyid="oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA"\
;alg="rsa-pss-sha512"\
;expires=1735693200\
;nonce="XSHtZVCThSIAksXsH9WBs6AtxtXC0eQGiIcUGSoJstFs8lAWakjhrfwzLhyjtme5iXMZvmFWqDEs6cT3Jf+BbQ=="\
;tag="web-bot-auth"
Signature: sig2=:I1QWNzGXdP1a4dSvOHLCVOOanEYHDk+ZsVxM9MLX/p4ko69ghKwR5EOtAD96g7g4GWP7lmpM/jFAf9q8EFRDTPLjUXySwMv4YPgabv2LQihTJG2y8a2m6IGltyruwQNiqSJVUuRaG9+b17CGmAMFZh30X6GXLdQJrCARpeTqPwp2DC+a8haDE/VE5EruqzjA5/2mKwvrkzkSqeW5tOVtFwWRRHIOidquf/8Je6kM9mhgkg4arudLA5SL4wyyYE1jURIgcOl8agrfdJ5Def23DIRtiOLRa8jT9cpTLFAuFHN+mrZA/LH9h0gSIg1cPb+0cMASee5uku1KjWcFer7jWA==:
¶
The test vectors in this section use the Ed25519 key defined in Appendix B.1.4 of [HTTP-MESSAGE-SIGNATURES]. This section include non-normative test vectors that may be used as test cases to validate implementation correctness.¶
This example presents a minimal signature using the ed25519 algorithm over test-request. The request does not contain
a Signature-Agent header.¶
The corresponding signature base is:¶
NOTE: '\' line wrapping per RFC 8792
"@authority": example.com
"@signature-params": ("@authority")\
;created=1735689600\
;keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"\
;alg="ed25519"\
;expires=4889289600\
;nonce="zIW8+cdmA3vdYagbxojpONwa/l0EKJ/O3/wD486VvsQjO/RxPaSt6ZxvQaMcQzNnqKN/mQ6hpGiFro2L2qkz5A=="\
;tag="web-bot-auth"
¶
This results in the following Signature-Input and Signature header fields being added to the message under the label sig1:¶
NOTE: '\' line wrapping per RFC 8792
Signature-Input: sig1=("@authority")\
;created=1735689600\
;keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"\
;alg="ed25519"\
;expires=4889289600\
;nonce="zIW8+cdmA3vdYagbxojpONwa/l0EKJ/O3/wD486VvsQjO/RxPaSt6ZxvQaMcQzNnqKN/mQ6hpGiFro2L2qkz5A=="\
;tag="web-bot-auth"
Signature: sig1=:QKN4fTdIYfh82fvoZCQiQA1weuozfCS/Led2zTMbewMMqH8PI2Wsy/5c4ao6B6D09nraNQdBNOADg8aM1MqfCg==:
¶
This example presents a minimal signature using the ed25519 algorithm over test-request. The request contains
a Signature-Agent header.¶
The corresponding signature base is:¶
NOTE: '\' line wrapping per RFC 8792
"@authority": example.com
"signature-agent";key="agent2": "https://signature-agent.test"
"@signature-params": ("@authority" "signature-agent";key="agent2")\
;created=1735689600\
;keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"\
;alg="ed25519"\
;expires=4889289600\
;nonce="n9p433xm+NJ3ph3upfBIGmsuwHw387YV7Q/F+6BSpGCVjYCqQw6rznNA8PVVLySrAWsv0hQtFioQb6E1YsauiA=="\
;tag="web-bot-auth"
¶
This results in the following Signature-Input and Signature header fields being added to the message under the label sig2:¶
NOTE: '\' line wrapping per RFC 8792
Signature-Agent: agent2="https://signature-agent.test"
Signature-Input: sig2=("@authority" "signature-agent";key="agent2")\
;created=1735689600\
;keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"\
;alg="ed25519"\
;expires=4889289600\
;nonce="n9p433xm+NJ3ph3upfBIGmsuwHw387YV7Q/F+6BSpGCVjYCqQw6rznNA8PVVLySrAWsv0hQtFioQb6E1YsauiA=="\
;tag="web-bot-auth"
Signature: sig2=:RdNFx5Bj6au3YgAMQL/RzmUlZE8QZLIaXGRpw985hWnwPfMxT228NMk6ehRS1PSl4e8PhbNZACSanGdhEwYCCg==:
¶
Retained for implementers migrating to the dictionary form (Section 5.2.1). Do not copy it into new deployments.¶
This example presents a minimal signature using the ed25519 algorithm over test-request. The request contains
a Signature-Agent header.¶
The corresponding signature base is:¶
NOTE: '\' line wrapping per RFC 8792
"@authority": example.com
"signature-agent": "https://signature-agent.test"
"@signature-params": ("@authority" "signature-agent")\
;created=1735689600\
;keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"\
;alg="ed25519"\
;expires=1735693200\
;nonce="e8N7S2MFd/qrd6T2R3tdfAuuANngKI7LFtKYI/vowzk4lAZYadIX6wW25MwG7DCT9RUKAJ0qVkU0mEeLElW1qg=="\
;tag="web-bot-auth"
¶
This results in the following Signature-Input and Signature header fields being added to the message under the label sig2:¶
NOTE: '\' line wrapping per RFC 8792
Signature-Agent: "https://signature-agent.test"
Signature-Input: sig2=("@authority" "signature-agent")\
;created=1735689600\
;keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"\
;alg="ed25519"\
;expires=1735693200\
;nonce="e8N7S2MFd/qrd6T2R3tdfAuuANngKI7LFtKYI/vowzk4lAZYadIX6wW25MwG7DCT9RUKAJ0qVkU0mEeLElW1qg=="\
;tag="web-bot-auth"
Signature: sig2=:jdq0SqOwHdyHr9+r5jw3iYZH6aNGKijYp/EstF4RQTQdi5N5YYKrD+mCT1HA1nZDsi6nJKuHxUi/5Syp3rLWBA==:
¶
This draft has a couple of public implementations. A demonstration server has been deployed to https://http-message-signatures-example.research.cloudflare.com/.¶
It uses ed25519 example signing and verifying keys defined in Appendix B.1.4 of [HTTP-MESSAGE-SIGNATURES].¶
draft-meunier-webbotauth-httpsig-protocol-00¶
Chrome MV3 (TypeScript)¶
Cloudflare Workers (TypeScript)¶
Rust binaries (Rust)¶
draft-meunier-web-bot-auth-architecture-03¶
Puppeteer script (JavaScript)¶
Guzzle middleware (PHP)¶
Python script (Python)¶
Bot-Authentication (Python)¶
HTTPie plugin (Python)¶
Web scrapers (scrapy/crawl4ai) (Python)¶
HUMAN Verified AI Agents (Python)¶
draft-meunier-webbotauth-httpsig-protocol-00¶
Cloudflare Workers (TypeScript)¶
draft-meunier-web-bot-auth-architecture-03¶
Caddy plugin (Go)¶
Apache module (C)¶
The editor would also like to thank the following individuals (listed in alphabetical order) for feedback, insight, and implementation of this document - Marwan Fayed, Maxime Guerreiro, Scott Hendrickson, Jonathan Hoyland, Nikhil Kandoi, Akshat Mahajan, Mark Nottingham, Eugenio Panero, Lucas Pardue, Malte Ubl, Loganaden Velvindron, Tanya Verma.¶
draft-meunier-webbotauth-httpsig-protocol-01¶
Add an Identifiers and Trust Model section: opaque and domain binding modes.¶
Describe the document as a protocol throughout (was: architecture).¶
Fold draft-meunier-webbotauth-httpsig-directory into this document with its
IANA registrations, and move to Standards Track.¶
Anchor identity on the resolved Signature-Agent URL rather than the key.
Rotation is the same URL serving a new key; the thumbprint identifies only
when no URL is sent. A directory value is an origin, identifiers are
normalized before comparison, and kid equals the key thumbprint.¶
Define attribution: lookup on the (URL, key) pair, what redistributed key material must carry, which source wins when two disagree, what a failed resolution means, a bound on the resolution behind it, and rejection of directory response signatures dated in the future.¶
When chaining, an outer signature covering an inner signature MUST also
cover its signature-input, signature-agent, and every component the inner
signature covered. Verifiers validate each signature independently.¶
Say what a signature does not reach: not the body without Content-Digest,
and not the method or path when only @authority is covered. Correct the
relabeling guidance, and note how to migrate from the sf-string form.¶
Move the domain binding to an appendix. Add objectives, the relationship with anonymous bot authentication, a use case to identifier mapping, what an unsigned request tells a verifier, and the worst case for a compromised key.¶
Note how field compression treats reused and per-request signatures, and why that is not a reason to widen coverage.¶
draft-meunier-webbotauth-httpsig-protocol-00¶
Rename draft from draft-meunier-web-bot-auth-architecture.¶
Add SSRF guidance for Signature-Agent directory fetches.¶
Add deployment guidance for verifier outcomes, directory fetches, caching, retry, rollout, proxies, CORS, and observability.¶
Add guidance for test keys, static signatures, and discovery failures.¶
Add multiple Web Bot Auth signatures and an example.¶
Add typed Signature-Agent discovery examples for directory, jwks_uri,
and cimd.¶
Group implementations by the draft version that added them.¶
Clarify that Signature-Input keyid selects the key and Signature-Agent
points to candidate key material.¶
Note Signature-Key as an optional discovery header.¶
Align examples with published test-vector fixtures.¶
Fix typos.¶
draft-meunier-web-bot-auth-architecture-05¶
Add Sandor Major as an author.¶
Add session protocol considerations.¶
Update HTTP Message Signatures test vectors.¶
Keep legacy Signature-Agent string examples for implementers migrating to
dictionary members.¶
draft-meunier-web-bot-auth-architecture-04¶
Change Signature-Agent to a Structured Fields dictionary.¶
Add a security consideration for intermediaries that relabel
Signature-Agent members.¶
Allow @target-uri as a replacement for @authority.¶
Add contributors.¶
Add implementations.¶
Remove the purpose field from the Web Bot Auth example.¶
draft-meunier-web-bot-auth-architecture-03¶
Update the Linzer example URL.¶
Fix the section reference and name for status code 429.¶
Fix typos.¶
draft-meunier-web-bot-auth-architecture-02¶
Add response status codes.¶
Add references for readability.¶
Add text about signing extra headers.¶
Add TLS guidance to Security Considerations.¶
Add RSASSA-PSS examples.¶
Update acknowledgments.¶
Add PHP, Python, Ruby, and Rust implementations.¶
Fix Signature-Agent in the architecture diagram to use Structured Fields.¶
Fix test vectors to use Structured Fields for Signature-Agent.¶
Fix typos.¶
draft-meunier-web-bot-auth-architecture-01¶
Require clients to sign Signature-Agent when it is present.¶
Add test vectors for requests with and without Signature-Agent.¶
Fix the example diagram.¶
Add reverse proxy security considerations.¶
Update text about why an origin may request a new signature.¶
Update nonce validation wording and uniqueness requirements.¶
Add acknowledgments.¶
draft-meunier-web-bot-auth-architecture-00¶