> ## Content Index
> Fetch the complete content index at: https://roundproxies.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Cloudflare Error 1010: What Triggers It and How to Fix It
- URL: https://roundproxies.com/blog/cloudflare-error-1010/
- Published: 2025-11-25T16:13:35.000Z
- Updated: 2026-09-24T12:26:18.000Z
- Description: Fix Cloudflare Error 1010: Browser fingerprint blocked. Learn solutions for visitors, site owners & developers. Bypass guide.
- Author: Marius Bernard
- Tags: Cloudflare, #dated-69257e8d26f439f88a959c35

Your script passed every local test. Then it hit a Cloudflare-fronted host and came back with a 403 and a 16-byte body that reads `error code: 1010`.

Cloudflare error 1010 means the site's Browser Integrity Check rejected your request headers. The User-Agent is the usual suspect.

Most guides answer with stealth browsers and residential proxies. That's heavy machinery aimed at the wrong layer.

This guide shows what a 1010 looks like on the wire and how it differs from Cloudflare's other blocks. Then come the fixes, for developers, site owners, and regular visitors.

## What is Cloudflare error 1010?

Cloudflare error 1010 is an access-denied response from Cloudflare's Browser Integrity Check, a feature that's on by default. It fires when your request headers look like they came from a known bot, spammer, or HTTP library, most often because the User-Agent is missing or non-standard. Fix it by sending the headers a real client would send.

Cloudflare's [Browser Integrity Check documentation](https://developers.cloudflare.com/waf/tools/browser-integrity-check/) describes the feature in two sentences. It denies requests carrying headers that spammers commonly abuse, and it challenges clients with a missing or non-standard User-Agent.

BIC is on by default. Plenty of site owners have never looked at the setting.

That's why API endpoints and webhook receivers throw 1010s nobody planned for.

The check happens at Cloudflare's edge. Your request never reaches the origin server, so the site's application logs won't show it.

Nothing in Cloudflare's description involves running JavaScript on your machine. BIC judges the request itself.

So a bare HTTP client gets rejected on its first request, with no challenge page in between.

Cloudflare doesn't publish which strings trip the check. Public bug reports keep pointing at the same culprits, though: default library User-Agents like `Python-urllib/3.x`, `Go-http-client/1.1`, and `libwww-perl`, or no User-Agent at all.

## What a 1010 response looks like on the wire

A browser gets an HTML block page with "Access denied," a Ray ID, and the line about your browser's signature. A script gets far less.

The raw response below is trimmed from a [public GitLab bug report](https://gitlab.com/gitlab-org/gitlab/-/issues/223293), where a `Python-urllib/3.8` client hit the block:

```http
HTTP/2 403
content-type: text/plain; charset=UTF-8
content-length: 16
server: cloudflare
cf-ray: 5a626dcaef7fcc62-ZRH

error code: 1010

```

Three details matter. The status is a plain 403, the body is 16 bytes of text, and `server: cloudflare` tells you the origin never answered.

If your code only logs status codes, a 1010 looks like any other 403, and it's easy to lose an hour debugging auth before anyone reads the body.

A developer on the higgsfield-api-skill repo rotated an API key and topped up a wallet before spotting the same 1010 on every endpoint, unauthenticated ones included.

Another team watched an LLM proxy report the block as a payment failure.

You can reproduce the block from a terminal. The first command sends a library-style User-Agent, the second a browser-style one:

```bash
# Library-style UA: prints "error code: 1010" on a site with BIC enabled
curl -s -A "Python-urllib/3.12" https://example.com/ | head -c 100; echo

# Browser-style UA: should print a normal status code such as 200
# Paste your own browser's UA from chrome://version
curl -s -o /dev/null -w "%{http_code}\n" \
  -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36" \
  https://example.com/

```

Swap `example.com` for your target. If the first command prints the 1010 body and the second prints 200, the block keys on the User-Agent and you're one header away from a fix.

## Cloudflare error 1010 vs 1020, 1015, and other blocks

Cloudflare returns several access-denied codes that look alike and have unrelated causes. Mixing them up is how people end up buying proxies for a header problem.

| Response                    | What blocked you                  | Keyed on                                         | Where the fix lives             |
| --------------------------- | --------------------------------- | ------------------------------------------------ | ------------------------------- |
| **1010**                    | Browser Integrity Check           | Request headers, mainly User-Agent               | Your client's headers           |
| **1020**                    | A custom WAF rule the owner wrote | Whatever the rule matches: path, country, UA, IP | The rule itself (ask the owner) |
| **1015**                    | A rate limiting rule              | Requests per time window                         | Slower pacing, spread load      |
| **1009**                    | Country or region block           | Geolocation of your IP                           | An IP in an allowed country     |
| **1005, 1006–1008**         | ASN or IP ban                     | Your network or IP address                       | A different IP                  |
| **"Just a moment..." page** | Managed or JS challenge           | Browser environment, TLS, behavior               | A real browser engine           |

Your IP address decides 1009 and 1005–1008\. Cloudflare's description of 1010 doesn't mention IP addresses at all.

Say you run a price monitor against 5,000 product pages on a Cloudflare-fronted store. Your first run uses Python's default `urllib`, and every request returns 1010 because BIC rejects the UA.

You set a browser User-Agent and the 1010s stop. Around page 400, you start getting 1015s: the store has a rate limiting rule, and you're sending ten requests a second.

You add delays. Then the JSON endpoints under `/api/` return 1020: the owner wrote a custom rule blocking them for anonymous traffic.

That one isn't yours to fix, so you parse the HTML pages instead.

Last, the US storefront returns 1009 to your server in Germany. That's the only block in the whole run where a different IP helps.

Four blocks, four different fixes, and a new IP solved one of them. If you're stuck on a 1015 right now, the [Cloudflare error 1015 guide](https://roundproxies.com/blog/cloudflare-error-1015/) covers pacing.

## Fixes at a glance

| Your situation                                  | Likely cause                           | First fix                     | Rough effort |
| ----------------------------------------------- | -------------------------------------- | ----------------------------- | ------------ |
| Script or API client gets error code: 1010      | Default library UA, or none            | Set a User-Agent              | 2 minutes    |
| Headless browser gets a 1010                    | HeadlessChrome in the UA               | Override the UA               | 5 minutes    |
| Your webhook receiver rejects a partner's calls | BIC on your own zone                   | Skip BIC for that path        | 10 minutes   |
| Regular browser shows the 1010 page             | An extension or tool rewriting headers | Disable extensions, retest    | 5 minutes    |
| Fixed the UA, now see a challenge page          | Bot detection, a separate system       | Browser TLS or a real browser | Longer       |

## How to fix Cloudflare error 1010 in your code

The fix is usually one header. The real work is proving which one, so you don't spend a day on stealth tooling for a problem a string solves.

### Step 1: Confirm you're looking at a 1010

Several Cloudflare blocks can return a 403\. This helper reads the response and names the block, so your logs say "1010" instead of "403."

```python
import re
import requests

def classify_cf_block(resp: requests.Response) -> str:
    """Name the Cloudflare block behind a response, if there is one."""
    if "cloudflare" not in resp.headers.get("Server", "").lower():
        return "not served by cloudflare"
    if resp.headers.get("cf-mitigated") == "challenge":
        return "challenge page"  # a JS/managed challenge, not a 10xx error
    # Plain-text bodies say "error code: 1010"; HTML block pages use a cf-error-code span
    match = re.search(r'error code:?\s*(\d{4})|cf-error-code">(\d{4})', resp.text[:5000], re.I)
    if match:
        return f"error {match.group(1) or match.group(2)}"
    return f"cloudflare {resp.status_code}, no error code in body"

resp = requests.get("https://example.com/", timeout=15)
print(resp.status_code, classify_cf_block(resp), resp.headers.get("cf-ray"))

```

Log the `cf-ray` value next to the result. If you ever need the site owner to allowlist you, the Ray ID is the first thing they'll ask for.

### Step 2: Find the header that trips it

Test candidate User-Agents one at a time against the same URL. The pattern that comes back tells you whether you're facing a UA blocklist or something else.

```python
import requests

URL = "https://example.com/"  # your target
CANDIDATES = {
    "no UA": None,  # None tells requests to drop the header entirely
    "python-requests": requests.utils.default_user_agent(),
    "urllib": "Python-urllib/3.12",
    "go": "Go-http-client/1.1",
    "curl": "curl/8.5.0",
    "descriptive": "acme-sync/1.0 (+https://acme.example/contact)",
    # Replace with the UA from your own browser (chrome://version)
    "chrome": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
              "(KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
}

for name, ua in CANDIDATES.items():
    r = requests.get(URL, headers={"User-Agent": ua}, timeout=15)
    verdict = "1010" if "error code: 1010" in r.text[:200] else "passed"
    print(f"{name:16} {r.status_code}  {verdict}")

```

If the library rows fail and the last two pass, you've found your fix. If every row fails, Chrome included, the block isn't about the UA; skip ahead to [Troubleshooting](https://claude.ai/chat/d82809f1-c3a9-45a9-9e03-ed15440c791c#troubleshooting).

Results vary by site. In one public report, `curl/8.5.0` got a 200 from an API that returned a 1010 to `Python-urllib`, so don't assume curl passes everywhere.

### Step 3: Send a real User-Agent

What you set depends on what you are to the site.

For API clients, integrations, and webhooks, use an honest UA with a contact URL, like `yourapp/1.0 (+https://yourapp.example)`.

In public reports, strings like that cleared 1010s on API hosts that blocked `Python-urllib`. They also let the operator find you.

For scraping pages built for browsers, use a current browser UA. Copy it from your own browser's `chrome://version` page rather than from a list that went stale years ago.

Python's standard library `urllib`, whose default UA shows up in more of these reports than any other:

```python
import urllib.request

req = urllib.request.Request(
    "https://api.example.com/v1/status",
    # Replaces the default "Python-urllib/3.x"
    headers={"User-Agent": "acme-sync/1.0 (+https://acme.example/contact)"},
)
with urllib.request.urlopen(req, timeout=15) as resp:
    print(resp.status, resp.read(200))

```

Node 18+ with the built-in `fetch` (save it as `.mjs` so top-level `await` works):

```javascript
const res = await fetch("https://api.example.com/v1/status", {
  headers: { "User-Agent": "acme-sync/1.0 (+https://acme.example/contact)" },
});
// A 403 with "error code: 1010" here means the UA still isn't accepted
console.log(res.status, (await res.text()).slice(0, 200));

```

Go's `net/http`, where the default is `Go-http-client/1.1`:

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.example.com/v1/status", nil)
	req.Header.Set("User-Agent", "acme-sync/1.0 (+https://acme.example/contact)")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(io.LimitReader(resp.Body, 200))
	fmt.Println(resp.StatusCode, string(body))
}

```

Each snippet sets the header on one request. In a real client, set it once on a shared session so no code path falls back to the default.

In `requests`, that's `session.headers["User-Agent"] = ...`.

### Step 4: Fix the User-Agent in headless browsers

Headless Chromium reports itself as `HeadlessChrome` in the User-Agent unless you override it. No human browser sends that string, which makes it an easy signature to block.

This Playwright script reads the default UA, then swaps only the headless token:

```python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    default_ua = browser.new_page().evaluate("navigator.userAgent")
    print("default:", default_ua)  # includes "HeadlessChrome" in headless mode

    # Keep the real version number; only drop the headless token
    context = browser.new_context(user_agent=default_ua.replace("HeadlessChrome", "Chrome"))
    page = context.new_page()
    resp = page.goto("https://example.com/")
    print(resp.status, page.evaluate("navigator.userAgent"))
    browser.close()

```

Swapping the token keeps the version matched to the engine you're running. A hardcoded Chrome/120 string on a much newer engine is its own inconsistency. In Puppeteer, `page.setUserAgent()` does the same job.

Client hints (`Sec-CH-UA`) can still carry the headless brand after the override. Log one request with `page.on("request", ...)` and read what the headers say.

### When the header fix gets you a different error

Sometimes the 1010 disappears and a "Just a moment..." page or a bare 403 shows up instead.

You've passed BIC and reached Cloudflare's bot detection, a separate system.

Bot detection checks things BIC doesn't, including whether your TLS handshake matches the browser your UA claims to be.

A Python client wearing a Chrome UA fails that comparison. Our explainer on [TLS fingerprinting](https://claude.ai/blog/what-is-tls-fingerprint/) covers how it works.

The free, open-source `curl_cffi` library sends a real browser's TLS and HTTP/2 fingerprint from Python:

```python
from curl_cffi import requests as cffi_requests

# impersonate="chrome" matches Chrome's TLS handshake, HTTP/2 settings, and default headers
resp = cffi_requests.get("https://example.com/", impersonate="chrome", timeout=15)
print(resp.status_code, resp.headers.get("cf-ray"))

```

If that still lands on a challenge, you need a real browser engine. The full playbook lives in our guide on [how to bypass Cloudflare](https://claude.ai/blog/bypass-cloudflare/).

## Why proxies and stealth plugins won't fix a 1010

Most advice for this error recommends rotating residential proxies. Roundproxies sells residential proxies, and they won't clear a 1010.

BIC judges the request, and the request looks identical from a home IP in Ohio or a datacenter in Frankfurt.

Rotate through 10,000 IPs with `Python-urllib` as your UA and you'll collect 10,000 copies of `error code: 1010`.

Stealth plugins miss for a similar reason. Tools like undetected-chromedriver and puppeteer-extra-plugin-stealth patch JavaScript-visible properties such as `navigator.webdriver`, and a header check never looks at those.

Both earn their keep elsewhere. IP quality matters for 1009, 1005–1008, and 1015; stealth patches matter against JS challenges. Save them for the errors that need them.

Random User-Agent rotation doesn't help either. One valid, current UA clears BIC; cycling through a list adds chances to pick a stale string.

If you rotate for other reasons, [rotate User-Agents the right way](https://roundproxies.com/blog/user-agent-rotation/) so each UA matches the rest of the request.

## How to fix error 1010 as a website owner

If users, partners, or your own cron jobs report 1010s, the cause is BIC on your zone. The fixes below run from narrow to blunt.

### Reproduce it first

Run the two curl commands from earlier against your own domain. If the library UA gets `error code: 1010` and the browser UA gets through, BIC is your culprit.

### Look up the Ray ID

Ask the reporter for the Ray ID on the block page, or the `cf-ray` header from their failed request. Search for it in your dashboard's Security Events.

The matching event should name the security feature that acted on the request. If it names something other than Browser Integrity Check, you're chasing a different problem.

### Skip BIC for one path, not the whole zone

Cloudflare supports skipping Browser Integrity Check with a custom rule that uses the **Skip** action. Scope the rule as tightly as you can.

For a webhook receiver, match the exact path and method:

```
(http.request.uri.path eq "/api/webhooks/courier" and http.request.method eq "POST")

```

Set the action to Skip, select Browser Integrity Check in the list of components to skip, and deploy. Then ask the sender to retry.

For a partner with fixed egress IPs, match their range plus your API prefix:

```
(ip.src in {203.0.113.0/24} and starts_with(http.request.uri.path, "/api/"))

```

Don't skip on User-Agent alone. Anyone can send `CFSCHEDULE` or `ColdFusion`, so a UA-based exception is an open door with a sign on it.

Once BIC stops screening a webhook path, verify the sender's signature (HMAC or whatever the provider offers) inside your handler.

Configuration Rules can also switch BIC off for a hostname or path. Use whichever rule type your team already manages.

### Turn BIC off globally

In the dashboard, go to Security, then Settings, and switch off Browser integrity check. On an API-only zone where every client is a script, that's often the right call.

On a zone that serves HTML to the public, I'd keep BIC on and use scoped skips. It filters a lot of junk traffic for zero effort.

## How to fix error 1010 as a regular visitor

If you see the "Access denied" page in a normal browser, something is making your requests look non-standard. Work through these in order and retest after each:

1. Disable extensions that touch headers: User-Agent switchers, header editors like ModHeader, and aggressive privacy add-ons. A private window with extensions off is the fastest test.
2. Update your browser. An old version sends a UA string that stands out against current traffic.
3. Try a mainstream browser. Hardened forks and the in-app browsers inside other apps sometimes send unusual headers.
4. Try another network. Some corporate proxies and security tools rewrite headers on the way out.
5. Contact the site owner with the Ray ID from the block page. Only they can change the setting, and a WHOIS lookup can surface contact details if the site lists none.

Clearing cookies rarely helps with a 1010, because the check reads each request on its own. It won't hurt to try.

A VPN is a less likely cause than most guides suggest. Its IP isn't what Cloudflare's docs say BIC evaluates, though a VPN's browser extension can alter headers.

## Troubleshooting

### "error code: 1010" persists after setting a User-Agent

**Why:** your client may not send what you think. Libraries sometimes set the header on one code path and not another, and redirects or retries can rebuild a request with defaults.

**Fix:** point the client at a local listener and read the raw request. Run `nc -l 8080` in one terminal (`nc -l -p 8080` on some Linux builds).

Then send your request to `http://localhost:8080/`.

Compare those headers line by line with what your browser sends to the same site.

![Raw HTTP request headers captured with netcat, showing a Python-urllib User-Agent that triggers Cloudflare error 1010](https://claude.ai/chat/raw-request-headers-netcat.webp)

### It works in curl but fails in Python

**Why:** curl sends `curl/<version>` and Python's standard library sends `Python-urllib/<version>`. Sites can block these strings independently, so one passes while the other fails.

**Fix:** set the UA explicitly in Python (Step 3) and rerun the bisect script to confirm.

### A third-party webhook to your site gets 1010

**Why:** the sender's HTTP client uses a default or blank UA, and BIC on your zone rejects it before your app sees the call. Disabling Bot Fight Mode alone won't change that.

**Fix:** add the path-scoped skip rule from the owner section, and make sure it names Browser Integrity Check specifically. Keep signature verification in your handler.

### Every User-Agent fails, including a real Chrome string

**Why:** the block may not be about the UA. Another header could be the trigger, or you're seeing a 1020 custom rule with the same "Access denied" styling.

**Fix:** run the Step 1 classifier on the response. If it says 1020, the [Cloudflare error 1020 guide](https://roundproxies.com/blog/cloudflare-error-1020/) is your next stop.

If it says 1010, strip the request to minimal headers and add them back one at a time.

## A note on responsible use

Fixing a 1010 on your own API client, webhook, or monitoring job is routine maintenance. Scraping someone else's site is a different conversation.

Getting past BIC doesn't mean the owner approves of your scraper.

Check robots.txt and the terms of service, keep request rates polite, and leave personal data alone unless you have a lawful basis to collect it.

## FAQ

### What does "error code: 1010" mean?

It means Cloudflare's Browser Integrity Check rejected your request's headers, usually the User-Agent. The response is a 403 with a short plain-text body.

### How long does Cloudflare error 1010 last?

It has no timer. Cloudflare evaluates each request separately, so the block lasts exactly as long as you keep sending the same headers. Change the headers and the next request can pass.

### Is error 1010 an IP ban?

No. Cloudflare reports IP bans as 1006, 1007, or 1008, and ASN bans as 1005\. Error 1010 keys on request headers, which is why switching IP addresses rarely makes it go away.

### Can a VPN cause error 1010?

Rarely on its own. A VPN changes your IP, which BIC doesn't evaluate per Cloudflare's docs. Its browser extension can rewrite headers, though, so test with extensions off.

### Why do I get error 1010 from an API instead of a website?

APIs often share a Cloudflare zone with the website, and BIC is on by default. Hand-rolled clients using `urllib` or Go's `net/http` often skip the UA.

Resend's API docs name a missing User-Agent as the likely cause of a 1010.

## Wrapping up

Treat a 1010 as a header problem until the evidence says otherwise. Read the response body, bisect the User-Agent, and set a real one on every request your client makes.

If the 1010 turns into a challenge page, you've reached Cloudflare's bot detection, and the bypass guide linked above picks up there.

For the other codes in the family, start with the comparison table. Only a few care about your IP.

Cloudflare's [error 1010 reference](https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/error-1010/) is worth bookmarking too. It's short, and it confirms the one thing most guides skip: this block belongs to Browser Integrity Check.