Cloudflare offers two distinct defenses that developers often conflate: the Browser Integrity Check (BIC) and CAPTCHA challenges. BIC is a passive, header-level check that blocks obviously non-browser traffic without any challenge — it simply returns a 403. CAPTCHA challenges (Turnstile, JavaScript challenges, Managed Challenges) present a solvable task that legitimate browsers can pass. The distinction matters because each requires a different handling strategy.
How they compare
| Feature | Browser Integrity Check | CAPTCHA Challenge |
|---|---|---|
| What it checks | HTTP headers only | Browser behavior + environment |
| JavaScript required? | No (header-level) | Yes |
| User sees | Nothing (pass) or 403 (fail) | Challenge widget or JS page |
| HTTP status on fail | 403 | 503 |
| Solvable? | No — fix headers or it blocks | Yes — solve CAPTCHA |
| Cookie issued | No | cf_clearance |
| CaptchaAI needed? | No — fix request headers | Yes |
| Default enabled | Yes (all Cloudflare plans) | No (configured per rule) |
| Applies to | Every request | Matching WAF rules only |
Browser Integrity Check (BIC)
BIC evaluates HTTP request headers to detect obviously non-browser traffic. It runs before any JavaScript or CAPTCHA challenge.
What BIC checks
| Check | What it looks for | Common failures |
|---|---|---|
| User-Agent validation | Missing, empty, or known-bad UA | python-requests/2.31, curl, wget |
| Header order | Headers in unusual order | Non-browser HTTP libraries |
| Missing browser headers | Accept, Accept-Language, Accept-Encoding missing |
Minimal HTTP clients |
| Known bot signatures | Spammer UAs, scanner UAs | Googlebot from non-Google IPs |
| Abuse IP blacklists | Known malicious IPs | Datacenter IPs on blocklists |
BIC flow
Request hits Cloudflare edge
↓
BIC evaluates HTTP headers:
├─ User-Agent present and valid?
├─ Required headers (Accept, Accept-Language) present?
├─ Header order consistent with browser?
└─ IP not on abuse list?
↓
Pass → Request forwarded to origin
OR
Fail → 403 Forbidden (no challenge offered)
Passing BIC
BIC is solved by sending proper browser-like headers. No CAPTCHA solution is needed:
import requests
# ❌ FAILS BIC — minimal headers
response = requests.get("https://cloudflare-protected.com")
# User-Agent: python-requests/2.31.0
# Result: 403 Forbidden
# ✅ PASSES BIC — browser-like headers
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
}
response = requests.get("https://cloudflare-protected.com", headers=headers)
# Result: 200 OK (passed BIC)
Node.js
const axios = require("axios");
// Browser-like headers to pass BIC
const headers = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0",
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
Connection: "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
};
async function fetchWithBIC(url) {
const response = await axios.get(url, { headers });
console.log(`Status: ${response.status}`);
return response.data;
}
CAPTCHA challenges
CAPTCHA challenges run after BIC passes. They require JavaScript execution and present a solvable challenge:
Types of Cloudflare CAPTCHA challenges
| Challenge type | Trigger | Visible to user? | CaptchaAI method |
|---|---|---|---|
| Turnstile widget | Embedded on page by developer | Widget on form | turnstile |
| Managed Challenge | WAF rule match | Adaptive (invisible/checkbox) | turnstile |
| JavaScript Challenge | IUAM or WAF rule | "Checking your browser" page | cloudflare_challenge |
| Interactive Challenge | High suspicion | Full-page challenge | cloudflare_challenge |
Challenge flow (differs from BIC)
Request passes BIC check
↓
WAF rules evaluate bot score, IP, path
↓
Rule triggers CAPTCHA challenge (HTTP 503)
↓
Browser executes JavaScript challenge
↓
Challenge solved → cf_clearance cookie issued
↓
Subsequent requests pass with cookie
Distinguishing BIC blocks from CAPTCHA challenges
When your request is blocked, identifying the type determines the fix:
import requests
def diagnose_cloudflare_block(url):
"""Determine if block is BIC or CAPTCHA."""
# Intentionally use minimal headers to trigger BIC
minimal_response = requests.get(url, timeout=15, allow_redirects=False)
# Use browser headers to bypass BIC
browser_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Accept": "text/html,application/xhtml+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
}
browser_response = requests.get(
url, headers=browser_headers, timeout=15, allow_redirects=False
)
result = {
"minimal_headers_status": minimal_response.status_code,
"browser_headers_status": browser_response.status_code,
}
if minimal_response.status_code == 403 and browser_response.status_code == 200:
result["diagnosis"] = "BIC only — fix headers to pass"
result["fix"] = "Add browser-like headers"
elif minimal_response.status_code == 403 and browser_response.status_code == 403:
result["diagnosis"] = "IP or WAF block — not just BIC"
result["fix"] = "Change IP or check WAF rules"
elif browser_response.status_code == 503:
html = browser_response.text
if "jschl" in html:
result["diagnosis"] = "IUAM JavaScript challenge"
result["fix"] = "Use CaptchaAI cloudflare_challenge method"
elif "cf-turnstile" in html or "challenge-platform" in html:
result["diagnosis"] = "Managed/Turnstile challenge"
result["fix"] = "Use CaptchaAI turnstile method"
else:
result["diagnosis"] = "Unknown Cloudflare challenge"
result["fix"] = "Inspect page source for challenge type"
elif browser_response.status_code == 200:
html = browser_response.text
if "cf-turnstile" in html:
result["diagnosis"] = "Page loads but has Turnstile widget"
result["fix"] = "Use CaptchaAI turnstile method for form submission"
else:
result["diagnosis"] = "No challenge — page accessible"
result["fix"] = "None needed"
return result
# Usage
diagnosis = diagnose_cloudflare_block("https://example-cf-site.com")
print(f"Diagnosis: {diagnosis['diagnosis']}")
print(f"Fix: {diagnosis['fix']}")
Common scenarios
Scenario 1: BIC block only
Request with python-requests UA → 403
Request with Chrome UA → 200 ✓
Fix: Add proper browser headers. No CaptchaAI needed.
Scenario 2: BIC pass, then CAPTCHA
Request with Chrome UA → 503 (challenge page)
Fix: Headers are fine. Use CaptchaAI to solve the challenge.
Scenario 3: BIC + CAPTCHA together
Request with python-requests UA → 403 (BIC block)
Request with Chrome UA → 503 (CAPTCHA challenge)
Fix: First fix headers, then solve CAPTCHA with CaptchaAI.
Scenario 4: Neither — IP block
Request with python-requests UA → 403
Request with Chrome UA → 403
Fix: Neither BIC nor solvable challenge. IP is blocked.
Try different IP/proxy.
Header best practices
Essential headers to pass BIC
CLOUDFLARE_SAFE_HEADERS = {
# Mandatory
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
# Strongly recommended
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
# Sec-Fetch headers (modern Chrome)
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
# Recommended for consistency
"Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": '"Windows"',
}
Headers that trigger BIC blocking
| Header | Why it fails |
|---|---|
User-Agent: python-requests/2.31.0 |
Known HTTP library identifier |
User-Agent: curl/7.81.0 |
CLI tool identifier |
Missing Accept header |
No browser omits Accept |
Missing Accept-Language |
All browsers send this |
User-Agent: "" (empty) |
Invalid — obvious automation |
User-Agent: Googlebot/2.1 from non-Google IP |
Fake bot identity |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| 403 with minimal headers, 200 with browser headers | BIC only | Use browser-like headers |
| 403 with all header combinations | IP blocked or WAF rule | Try different IP or proxy |
| 503 with challenge page | CAPTCHA challenge (not BIC) | Use CaptchaAI |
| 403 intermittently | Rate limiting or session-based | Lower request rate, maintain session |
| Headers correct but still 403 | TLS fingerprint check (JA3) | Use curl_cffi or real browser |
Frequently asked questions
Can I disable BIC for my site?
Yes. Site operators can disable BIC in Cloudflare dashboard under Security > Settings. It's enabled by default on all plans.
Does BIC check TLS fingerprints?
Not directly. BIC focuses on HTTP headers. However, Cloudflare's broader Bot Management (enterprise) does examine JA3/JA4 TLS fingerprints separately from BIC.
If I pass BIC, will I still get CAPTCHAs?
Yes. BIC and CAPTCHA challenges are independent layers. Passing BIC means your headers look legitimate. You may still trigger a CAPTCHA from WAF rules, bot scoring, or IUAM mode.
Does CaptchaAI help with BIC?
No — BIC doesn't present a solvable challenge. Fix your HTTP headers to pass BIC. CaptchaAI handles CAPTCHA challenges (Turnstile, JavaScript challenges, Managed Challenges) that appear after BIC passes.
How do Sec-Fetch headers affect BIC?
Modern Chrome sends Sec-Fetch-* headers automatically. Their absence doesn't always trigger BIC, but including them makes your request profile more consistent with a real browser.
Summary
Cloudflare's Browser Integrity Check blocks requests with obviously non-browser headers (403), while CAPTCHA challenges present solvable tasks (503). BIC is fixed by using proper browser-like headers — no CaptchaAI needed. CAPTCHA challenges require CaptchaAI's Turnstile or Cloudflare Challenge solver. Always diagnose the block type first: fix headers for BIC, use CaptchaAI for CAPTCHAs, and change IPs for hard blocks.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.