惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
L
LangChain Blog
H
Help Net Security
博客园_首页
T
Tailwind CSS Blog
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
雷峰网
雷峰网
Recent Announcements
Recent Announcements
D
DataBreaches.Net
U
Unit 42
Vercel News
Vercel News
I
InfoQ
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
Apple Machine Learning Research
Apple Machine Learning Research
S
SegmentFault 最新的问题
Jina AI
Jina AI
博客园 - 叶小钗
博客园 - 【当耐特】
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
Last Week in AI
Last Week in AI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Cloudflare cf_clearance: why it expires and how to stop t...
Bassem Shahin · 2026-06-17 · via DEV Community

Cloudflare cf_clearance: why it expires and how to stop the re-challenge loop

You solve the Cloudflare challenge, get a cf_clearance cookie, make your next request — and Cloudflare throws you straight back to the challenge. You solve it again, same thing. The scraper is technically "working," it's just stuck in a loop, burning a solve on every request and never getting to the actual page.

This is one of the most common Cloudflare problems, and it's almost never the challenge itself. It's how cf_clearance is issued and what it's tied to. Once you understand that, the loop has a clean fix.

What cf_clearance actually is

When you pass a Cloudflare challenge (managed challenge, JS challenge, or interactive Turnstile), Cloudflare issues a cf_clearance cookie. That cookie is your proof of passage — present it on subsequent requests and Cloudflare lets you through without re-challenging.

The catch is that cf_clearance is not a portable token you can reuse anywhere. It carries two constraints that cause the loop:

1. It has a TTL

cf_clearance expires. The window varies by site configuration (often tens of minutes, sometimes shorter under stricter settings or "Under Attack" mode). When it expires, the next request gets challenged again — expected behavior. The bug is when your code keeps replaying an expired cookie instead of noticing it's dead and minting a fresh one.

2. It's bound to your IP + User-Agent + TLS fingerprint

This is the one that traps people. Cloudflare binds the clearance to the exact context that solved the challenge:

  • the IP that passed it,
  • the User-Agent string presented, and
  • the TLS/JA3 fingerprint of the client.

Change any of those between getting the cookie and using it, and Cloudflare treats the cookie as invalid and re-challenges. That's why these patterns loop forever:

  • Rotating proxies mid-session — you solve on IP A, your next request goes out IP B, cookie rejected.
  • Solving in a real browser, then submitting from requests — the browser's TLS fingerprint and UA don't match your HTTP client, so the cookie you carefully obtained is dead on the first reuse.
  • Spoofing a UA that doesn't match the fingerprint — claiming Chrome while sending a Python-requests JA3 is itself a mismatch.

How to confirm it's the loop and not something else

Before fixing, log what you're actually getting back:

import requests

r = requests.get(url, headers=headers, cookies={"cf_clearance": clearance})
print(r.status_code)
print(r.headers.get("cf-mitigated")) # "challenge" => you got re-challenged
print("cf_clearance" in r.cookies) # did this response set a NEW one?

  • cf-mitigated: challenge (or a challenge-page body) → you're in the re-challenge loop; the cookie was rejected.
  • A flat 403 with a Cloudflare 1006/1007 error code → that's an IP ban, not a clearance problem; no cookie fixes it (you need a different egress IP).
  • A clean 200 → you're through; the cookie is valid for now.

The fix

The loop breaks when you stop reusing a stale/mismatched cookie and instead evict-and-re-mint on the challenge signal, with a pinned context:

  1. Pin one IP + one UA + one TLS fingerprint per session. Don't rotate the proxy mid-session. If you're submitting from Python, send a browser-matching TLS fingerprint (e.g. curl_cffi with impersonate) and the same UA you solved with — not the default requests fingerprint.
  2. Detect re-challenge by the response, not a timer. Cloudflare can invalidate early, so don't trust a fixed "valid for N minutes" assumption. When you see cf-mitigated: challenge, treat the stored clearance as dead, evict it, and re-mint.
  3. Re-mint with the same context that will reuse it. Whatever solves the challenge (a real browser or a solver) must produce a cookie usable by the same IP+UA+fingerprint that makes the real requests.
  4. Optionally refresh proactively just before the known TTL to avoid a user-facing stall.

A minimal session loop that respects all of this:

import curl_cffi.requests as cc

session = cc.Session(impersonate="chrome") # browser-matching TLS fingerprint
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/124.0 Safari/537.36"
session.headers["User-Agent"] = UA
PROXY = "http://user:pass@residential-ip:port" # one sticky IP for the session
session.proxies = {"http": PROXY, "https": PROXY}

def get(url, clearance):
    r = session.get(url, cookies={"cf_clearance": clearance} if clearance else {})
    if r.headers.get("cf-mitigated") == "challenge" or "challenge-platform" in r.text:
        clearance = mint_clearance(url, UA, PROXY) # re-mint with the SAME UA+IP
        r = session.get(url, cookies={"cf_clearance": clearance})
    return r, clearance

The point isn't the exact library — it's that the cookie, the UA, the IP, and the fingerprint all stay consistent, and a challenge response triggers a re-mint instead of another doomed retry.

If you offload the challenge to a solving service, the same rule applies: it has to mint the clearance against the IP+UA you'll reuse, or hand back a token you submit immediately in a matched context. CaptchaAI handles the Cloudflare challenge flow this way and is 2Captcha-API-compatible, so an existing client is mostly a base-URL change — and if you want to test it against your own target, the trial is free (3 days, no card).

TL;DR checklist

  • [ ] One IP + one UA + one TLS fingerprint, pinned for the whole session
  • [ ] Don't rotate the proxy mid-session
  • [ ] Submit with a browser-matching TLS fingerprint (not raw requests) if you solved in a browser
  • [ ] Detect re-challenge via cf-mitigated / challenge body, evict + re-mint — don't replay a dead cookie
  • [ ] Rule out a 1006/1007 IP ban first (different problem, needs a new IP)

Get those right and the loop turns into a single solve followed by clean 200s — which is how it's supposed to behave.