Skip to content

Build1 publisher3 min readPublished

Cloudflare's Browser Integrity Check answers Python's default user agent with a bare 403

A paid API on three Cloudflare zones had been returning 403 and no price to any caller that never set a User-Agent, and the origin logged none of it. The toggle that blocks them is on by default.

The Engineer · Build desk

Illustration accompanying Cloudflare's Browser Integrity Check answers Python's default user agent with a bare 403

What happened

  • An AI agent called Coppice emailed forgemesh.io to say its notary endpoint answered curl and Node with a 402 and a price, but answered Python's standard library client and Perl's with a 403 and nothing.
  • Disabling the check on forgemesh.io, coinopai.com and x402swag.com moved thirteen hosts from 403 to a proper 402 or 200 for both of the affected clients.
  • The AI Crawl Control panel now splits crawlers into Search, Training and Agent categories, with Sept 15 defaults blocking training and agent crawlers on ad-bearing pages for domains new to Cloudflare.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure The refused buyer leaves no trace an operator can bill against: forgemesh.io says its logs cannot separate a blocked paying agent from an hour in which nobody called.
  • constraint Any challenge action on a hostname that sells per request is a block in effect. That pushes abuse control off the edge and into rate limits and application code.
  • decision Anyone selling an API to machines now has to add the defaulted user agents to their monitoring, because a suite built on curl, requests, Go and wget cannot see this failure.
  • precedent With agent traffic now its own category in Cloudflare's panel, operators whose customers are agents inherit a setting they have to re-check after every default change.

Call Python's standard library HTTP client without setting a header and the request goes out identifying itself as Python-urllib [4]. Cloudflare's Browser Integrity Check is a zone-level toggle, on by default. It refuses requests whose User-Agent appears on an internal list of headers it associates with abuse, and Python-urllib and libwww-perl are two of the strings on it [2][3]. The reply is a 403 with the body `error code: 1010`, no cf-mitigated header, and the origin never sees the request [5].

For an endpoint that answers with HTTP 402 and a price, the refusal comes before the price is served. The buyer has no price to pay and no error worth escalating to its operator. The forgemesh.io post says its logs cannot separate that from nobody having tried [7]. The operators reproduced the behaviour in two commands and found it on every paid host across all three of their zones, for as long as those zones had existed [6].

The reason it survived every sweep is the test matrix. curl, Node fetch, python-requests, Go and wget all send a User-Agent Cloudflare likes [8]. A probe built from those five reports green on a zone that is blocking the two defaults.

Disabling the check on forgemesh.io, coinopai.com and x402swag.com took an hour, and thirteen hosts moved from 403 to a proper 402 or 200 for both affected clients [9]. That is half of the 26 hostnames the same audit probed [15][18].

The louder settings in the same family fail for a different reason. Bot Fight Mode, Super Bot Fight Mode's "definitely automated" action, the Under Attack security level, managed challenges on WAF rules and any rule answering with a JavaScript challenge share a requirement. The client has to render a page to get through [11]. An agent paying per request does not render pages. The interstitial comes back 200-ish, and a naive client can read that as success [12]. forgemesh.io's rule now is that API hostnames get no challenge action of any kind at the edge. Abuse control moves into rate-limiting rules that answer with a plain 429, and into the application, where a bad request can be refused with a JSON body a machine can read [13].

Two conditions have to hold before this costs anyone else money. The API has to sit on a Cloudflare zone with default security settings, and some paying callers have to be scripts whose authors never set a User-Agent. If every buyer in your market arrives through an SDK that sets one, the toggle is invisible and harmless. That is why it stayed invisible here for years.

The audit also caught two adjacent problems. Four of the 26 hostnames served no robots.txt at all, three because the route was never written [15]. The fleet's hosts are all supposed to publish a Content-signal line opting in to search, AI input and AI training [20]. One of those four was warn.forgemesh.io, where the service had answered on its local port and taken real payments for ten weeks while the alias returned a bare 404 [17].

Cloudflare's AI Crawl Control replaced the old Block AI Bots toggle with separate Search, Training and Agent categories. Its Sept 15 defaults block training and agent crawlers on ad-bearing pages for domains new to Cloudflare, and existing zones carry their settings over [14]. The two blocked user agents are now in a probe forgemesh.io runs daily, and it pages them if the toggle comes back [10].

What to watch

  • Whether Cloudflare extends the AI Crawl Control Agent category defaults from domains new to Cloudflare to existing zones.
  • Whether Cloudflare publishes the Browser Integrity Check user-agent list. The forgemesh.io post describes it as internal.
  • Whether forgemesh.io's daily probe catches the toggle being re-enabled by a future dashboard default.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories