---
title: "x402 security: eight failure modes to test | Settlens"
description: "Eight ways x402 payment endpoints fail quietly, from fail-open resources and forged signatures at /verify to drift and weak binding, and how to test each one."
url: https://settlens.com/blog/x402-security-failure-modes
last_updated: 2026-10-13
published: 2026-07-31
author: "Alessandro Gagliardi"
---

# x402 security: the payment failure modes every implementation should test

> **In short:** The eight x402 failure modes to test are: a resource that serves content for an invalid signature, a facilitator /verify that accepts a forged one, weak resource binding, drifting terms, malformed challenges, weak transport, long authorization windows and facilitator coverage gaps.

x402 turns HTTP 402 into a real payment. When an implementation is wrong, nothing crashes: content unlocks for free, or money goes to the wrong place. Here is what to test, and how.

x402 is a payment protocol built on HTTP. A client requests a resource and receives `402 Payment Required` with a `PAYMENT-REQUIRED` header that lists the accepted payment options. The client signs one of them and retries with a `PAYMENT-SIGNATURE` header. The resource server, usually with a facilitator's help, verifies and settles the payment before it serves the content. See [what x402 is](https://settlens.com/learn/what-is-x402) and [the three V2 headers](https://settlens.com/learn/x402-v2-headers) for the full picture.

When an x402 implementation is wrong, nothing crashes. The resource unlocks for a signature nobody checked. A recipient address changes between two requests. A facilitator says yes to a forged authorization. The HTTP traffic looks normal, the logs look normal, and the money goes the wrong way or never arrives.

This article walks through eight failure modes we test for, what each one looks like on the wire, and the Settlens check that catches it. Every transcript below is real output from the Settlens CLI against its bundled demo target, a local server with a deliberately broken route for each failure shown. None of these tests needs a wallet or a private key, and none of them calls a facilitator's `/settle` endpoint.

_Diagram: x402 V2 sequence between an agent, a paid resource and a facilitator, annotated with eight failure points: 1 the resource serves content for an invalid signature, 2 the facilitator accepts a forged signature at /verify, 3 to 7 problems in the 402 challenge (weak binding, drift, malformed content, transport, long authorization windows) and 8 facilitator capabilities that do not cover the advertised options. Settlens never calls /settle._

_Where an x402 V2 exchange can fail. Points 3 to 7 live in the 402 challenge and are visible to a passive scan; points 1 and 2 need the authorized active probe; point 8 compares the challenge with the facilitator's GET /supported._

## 1. Payment bypass: the resource fails open

The worst failure is also the simplest. The server checks that a `PAYMENT-SIGNATURE` header is _present_ and serves the content without verifying it. Anyone who sends arbitrary bytes in that header gets the paid resource for free. It is a fail-open bug and a direct revenue leak, and it often survives code review because the happy path works perfectly: real clients send real signatures, so the missing verification never shows up in normal traffic.

The only reliable way to find it is to send an invalid payment and watch what happens. With the endpoint owner's explicit approval, the Settlens active probe builds one intentionally invalid authorization from the first advertised payment option (for an EVM option, an all-zero 65-byte signature from the zero address) and retries the request with it. If the resource answers with any `2xx` status, `security.invalid_signature_fail_closed` fails at critical severity. A separate check, `transport.invalid_payment_status`, expects the status the HTTP transport defines: `400` for malformed payment data or `402` when verification or settlement fails.

**Active probe against a fail-open route (demo target)**

```text
$ settlens scan http://127.0.0.1:4402/bypass \
    --active --authorized --authorization-ref DEMO-APPROVAL-002
Settlens x402 assessment eafd7f6a
Targets 1 · Active 1 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/bypass
13 pass · 1 fail · 1 warn · 0 skip
  FAIL critical Invalid payment authorization is denied
       The endpoint returned HTTP 200 after an invalid payment authorization.
  WARN medium   Invalid payment uses a standard failure status
       Expected HTTP 400 or 402 for invalid payment, received HTTP 200.
```

The scan exits with code `1`, so a CI job stops here. Note the scope line: one active target, zero settlement calls. The probe proves the endpoint refuses a bad payment; it cannot create a good one.

> **Authorized testing only**
>
> The active probe sends traffic the endpoint owner must approve. Settlens refuses to run it without both `--active` and `--authorized` plus an `--authorization-ref` that names the approval, and it records that reference in the report.

## 2. The facilitator accepts a forged signature

Many resource servers delegate verification to a facilitator: they forward the payment payload to `POST /verify` and serve the content when the answer says `isValid: true`. If the facilitator's own verification is broken, every resource server that relies on it inherits a payment bypass, and none of them can see it from their own code.

When you pass `--facilitator` to an authorized active scan, Settlens sends the same invalid payload to the facilitator's `/verify`. It expects a conformant VerifyResponse, a JSON object with a boolean `isValid` (`facilitator.verify_response_schema`), and it expects that value to be `false` (`security.facilitator_rejects_invalid_signature`, critical). The demo target ships a deliberately broken facilitator route that approves everything:

**Facilitator that approves an all-zero signature (demo target)**

```text
$ settlens scan http://127.0.0.1:4402/secure \
    --facilitator http://127.0.0.1:4402/facilitator-accept \
    --active --authorized --authorization-ref DEMO-APPROVAL-003
Settlens x402 assessment 9fa4dcf9
Targets 1 · Active 1 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/secure
26 pass · 1 fail · 0 warn · 0 skip
  FAIL critical Facilitator /verify rejects an invalid signature
       The facilitator reported isValid=true for an intentionally invalid, all-zero signature.
```

Test the facilitator directly, not only through the resource. The two can disagree, and the disagreement is exactly where money goes missing. Our guide to [x402 facilitator security](https://settlens.com/learn/x402-facilitator-security) covers what `/verify`, `/settle` and `/supported` are each expected to do.

## 3. Weak payment-to-resource binding

Every x402 V2 challenge carries a `resource` object whose `url` says what is being paid for. When that URL does not match the URL the client requested, the client cannot tell what it is paying for. Common causes are mundane: one shared challenge for every route, a stale host name after a proxy or CDN change, or a query parameter dropped when the challenge is built. The effect is not mundane: a payment can be steered to, or reused for, a different resource than the one the agent asked for.

The check `binding.resource_url` compares `resource.url` with the requested URL, path and query included (the fragment is ignored), and fails at high severity when they differ. You can run it on a captured header with the free [x402 payment request checker](https://settlens.com/tools/x402-checker): paste the header and the URL you requested. On the server side, the matching control is to verify that the terms in a payment payload are the terms you issued for _this_ resource (amount, recipient, asset and network) rather than accepting any valid signature that pays the right address. A passive scan cannot observe that server-side logic; it can only confirm that the challenge itself is bound correctly.

## 4. Recipient, asset and price drift

The `payTo`, `asset` and `amount` fields decide who gets paid, in which token and how much. If they change between two identical requests, something is wrong: a dynamic pricing bug, two backends behind a load balancer serving different configurations, a recipient rotation nobody reviewed, or tampering. If they drift away from the values you reviewed, funds can be silently redirected or customers overcharged.

Settlens catches drift in two ways. `stability.payment_requirements` requests the resource several times (two by default) and compares the `resource`, `accepts` and `extensions` it receives, ignoring known ephemeral fields such as a Solana `recentBlockhash`. A change raises a warning. Local policy checks then pin the values you approved: `policy.recipient`, `policy.asset`, `policy.network`, `policy.scheme` and `policy.maximum_amount` fail at high severity when the advertised terms leave the reviewed set. The demo's drift route alternates its price on every request:

**Price that changes between identical requests (demo target)**

```text
$ settlens scan http://127.0.0.1:4402/drift
Settlens x402 assessment 56c93ec9
Targets 1 · Active 0 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/drift
12 pass · 0 fail · 1 warn · 0 skip
  WARN medium   Payment requirements remain stable
       Payment terms changed between identical unauthenticated requests.
```

A recipient that changes without a deploy is a red flag, not a feature. Our [guide to detecting payment drift](https://settlens.com/blog/detecting-x402-payment-drift) covers the stability check, policy pins and continuous monitoring in detail.

## 5. Malformed or non-standard challenges

Protocol drift breaks integrations without an obvious error. A server still emitting `x402Version: 1` where clients expect 2, an amount written as `"0.01"` instead of an integer string of atomic units, a network written as `base` instead of the CAIP-2 identifier `eip155:8453`, a `payTo` that is not a 20-byte address on an EVM chain, or the same payment option listed twice. The official SDK schema catches some of these, such as a network that is not CAIP-2, but accepts others: a well-formed version-1 object, a decimal amount, a malformed `payTo` or a duplicate option. Hand-written clients may accept any of them and interpret them differently. To your users, every variant looks like a mysterious payment failure.

A passive scan checks that the `PAYMENT-REQUIRED` header is present and decodes as Base64 JSON, that the pinned official `@x402/core` 2.28.0 SDK accepts the object, that the V2 core shape is there, and, with its own rules on top of the SDK, that every payment option is well formed and that no option is duplicated. If you are still moving an implementation off version 1, the [x402 V1 to V2 migration guide](https://settlens.com/learn/x402-v1-to-v2-migration) lists what changed on the wire. The demo's broken route answers 402 but forgets the header entirely:

**402 without a PAYMENT-REQUIRED header (demo target)**

```text
$ settlens scan http://127.0.0.1:4402/missing-header
Settlens x402 assessment fe4514e2
Targets 1 · Active 0 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/missing-header
4 pass · 1 fail · 0 warn · 1 skip
  FAIL high     PAYMENT-REQUIRED header is present
       The response does not include PAYMENT-REQUIRED.
  SKIP medium   Payment requirements remain stable
       Only one successful sample was available.
```

The stability check is skipped here because there was no decodable challenge to compare. A skip is not a pass: it means the prerequisite evidence was missing.

## 6. Transport weaknesses

Payment terms are only as trustworthy as the channel that carries them. Over plaintext HTTP, anyone on the network path can rewrite `payTo` or `amount` before the agent signs; `transport.https` warns about any non-loopback endpoint that is not served over HTTPS. A redirect moves the challenge away from the URL that was assessed, so Settlens never follows one: `transport.redirect` records the sanitized destination and warns instead. And a 402 response body should be small. Settlens stops reading at 1 MiB (`transport.response_body_limit`), because a very large challenge body wastes agent bandwidth and can mean the protected content is being sent before payment.

## 7. Long authorization windows and replay exposure

The `maxTimeoutSeconds` field is the maximum time the server allows for completing a payment. A long value invites long-lived authorizations, and the longer an authorization stays valid, the longer a leaked or intercepted one can be presented again. Settlens warns when any advertised value exceeds 3600 seconds (`freshness.timeout_window`, medium severity); keep it as short as settlement allows.

For the EVM `exact` scheme with its default EIP-3009 transfer method, each authorization is a signed message with a `validAfter`/`validBefore` window and a nonce, and the token contract refuses a nonce that was already used when the transfer settles. `/verify` commits no state, so until settlement nothing on-chain has consumed the nonce: a server that serves content after verification alone must make sure the same authorization is not accepted twice. Be precise about what a test can show here: Settlens checks the window of a captured authorization offline with `verify-payment`, and it checks that the advertised windows are bounded. It does not check whether a nonce was used on-chain and it does not test replay. Our [EIP-3009 explainer](https://settlens.com/learn/eip-3009) covers the authorization format.

## 8. Facilitator coverage gaps

A resource can advertise a scheme and network that its facilitator does not support. The agent signs a perfectly valid payment that can never be verified or settled, and the failure surfaces as a refused purchase. Settlens reads the facilitator's `GET /supported` and checks its schema, its x402 V2 capability and its signer metadata, then cross-checks every advertised scheme and network against the supported kinds (`facilitator.requirement_coverage`) and the published signers (`facilitator.signer_coverage`). The demo includes a facilitator that only supports Base mainnet while the resource asks for Base Sepolia:

**Facilitator that does not cover the advertised network (demo target)**

```text
$ settlens scan http://127.0.0.1:4402/secure \
    --facilitator http://127.0.0.1:4402/facilitator-empty
Settlens x402 assessment b92b9c27
Targets 1 · Active 0 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/secure
22 pass · 1 fail · 0 warn · 0 skip
  FAIL high     Facilitator covers advertised payment requirements
       1 advertised payment choice(s) are absent from facilitator capabilities.
```

## The failure modes at a glance

The table is generated from the Settlens check catalog, the same source the CLI, the reports and the [check reference](https://settlens.com/docs/checks) use. Mode tells you what a check needs: _offline_ checks run on the challenge content alone, _passive_ checks need live unauthenticated requests, _active_ checks need the authorized probe and _facilitator_ checks read `GET /supported`.

_Failure modes and the Settlens checks that cover them_

| Failure mode | Checks | Mode · highest severity |
| --- | --- | --- |
| 1. Fail-open resource | `security.invalid_signature_fail_closed` ; `transport.invalid_payment_status` | active · critical |
| 2. Facilitator accepts a forged signature | `security.facilitator_rejects_invalid_signature` ; `facilitator.verify_response_schema` | active · critical |
| 3. Weak payment-to-resource binding | `binding.resource_url` | offline · high |
| 4. Recipient, asset and price drift | `stability.payment_requirements` ; `policy.recipient` ; `policy.asset` ; `policy.maximum_amount` ; `policy.network` | passive, offline · high |
| 5. Malformed challenge | `transport.payment_required_header` ; `transport.payment_required_encoding` ; `schema.official_x402_sdk` ; `schema.payment_required` ; `schema.payment_requirements` ; `schema.duplicate_requirements` | passive, offline · high |
| 6. Transport weaknesses | `transport.https` ; `transport.redirect` ; `transport.response_body_limit` | passive · medium |
| 7. Long authorization windows | `freshness.timeout_window` | offline · medium |
| 8. Facilitator coverage gaps | `facilitator.requirement_coverage` ; `facilitator.signer_coverage` | facilitator · high |

## What these tests do not tell you

Each check reports observed behavior during the recorded window. A clean report does not prove that untested application code is free of vulnerabilities, that a real wallet signature will settle, that balances are sufficient or that a nonce is unused on-chain. Settlens does not call `/settle`, does not broadcast transactions and does not inspect the content you deliver after payment. It is evidence about the payment handshake, not a certification. The [assessment methodology](https://settlens.com/docs/methodology) spells out what each status and severity means.

Within that boundary, every failure above is deterministic to test and cheap to run on every release. Start with the passive checks, add the facilitator, then run the authorized probe once you have the owner's approval. The [practical testing checklist](https://settlens.com/blog/how-to-test-x402-endpoint) walks through the commands in order, and [a pilot](https://settlens.com/contact) runs them against your own endpoints with a report your partners can verify.

## Sources

1. [x402 protocol specification, version 2 (§5 types, §7 facilitator interface, §10 security considerations)](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md) — x402 Foundation
2. [x402 V2 HTTP transport: headers and error status codes](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md) — x402 Foundation
3. [Scheme exact on EVM: EIP-3009 authorization payload and verification](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md) — x402 Foundation
4. [EIP-3009: Transfer With Authorization](https://eips.ethereum.org/EIPS/eip-3009) — Ethereum Improvement Proposals
5. [CAIP-2: Blockchain ID specification](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) — Chain Agnostic Standards Alliance
