Explainer
Cloudflare WAF Bypass: What Gets an Automated Client Through
Looking for a Cloudflare WAF bypass? What each WAF action does to a scraper, which ones can be passed, which only the site owner can lift, and the request.
By ZeroCaptcha Engineering7 min readPublished
There is no general Cloudflare WAF bypass, and what gets an automated client through depends
on which action the site’s rule took. A challenge action (Managed, Non-Interactive or
Interactive Challenge) shows the “Just a moment…” page, and passing it earns a cf_clearance
cookie that lets later requests through. A Block action, such as error
1020, refuses the request, and only the site’s owner can lift it. A
rate limit (error 1015) means slow down. So the useful question is
not how to bypass the WAF, but which of the three you met, and whether you are allowed to be there.
This article sorts out what people mean by “bypass”, how to tell the actions apart from the response, and the full request that passes a WAF challenge, as Cloudflare documented it on 1 October 2026. Only automate sites you are allowed to: your own, a client’s, or one whose terms permit it. See responsible captcha automation.
What “bypass” usually means
Four different things get called a Cloudflare WAF bypass. Only two are something an automated client should do.
| What people mean | What it is | Should you |
|---|---|---|
| Passing the challenge | Running Cloudflare’s challenge to the end, as a browser does, and keeping the cf_clearance cookie it earns |
Yes, on sites you may automate. It is what the challenge asks for |
| Being exempted | The owner allows your address, skips rules for your traffic, or lets your bot in as a verified bot | Yes: it is the owner’s own decision, and the most reliable route for a partner or a long-running feed |
| Going around Cloudflare | Sending requests straight to the site’s origin server, past the WAF | No. It defeats protection the owner chose, and ZeroCaptcha does nothing of the kind |
| Disguising the client | Changing user agents, addresses or TLS fingerprints until a Block rule stops matching | No. A Block rule is a decision the owner made on purpose |
Cloudflare’s own words for the first route: the cf_clearance cookie “enables visitors to bypass
WAF Challenges” at its clearance level. That is the only bypass this article covers.
Which WAF action did you meet?
A Cloudflare WAF custom rule pairs an expression with an action. Its terminating actions are
Block (“Matching requests are denied access to the site”), and the three challenges: Managed
Challenge, Non-Interactive Challenge (API value js_challenge, often still called the JS
Challenge, and once the 5-second challenge) and Interactive Challenge. Rate limiting rules take
the same actions once a client goes over a count. What each one does to your client:
| Action | What your client gets | What gets it through |
|---|---|---|
| Managed Challenge | HTTP 403, cf-mitigated: challenge, the “Just a moment…” page |
Passing the challenge: a Managed or Interactive clearance |
| Non-Interactive Challenge (JS, the old 5-second challenge) | The same | Passing the challenge: a clearance of any level |
| Interactive Challenge | The same | Passing the challenge: an Interactive clearance |
| Block | Usually HTTP 403 with a 1xxx code, such as 1020, or the owner’s custom response | Nothing on your side: only the owner can change the rule |
| Rate limiting rule | HTTP 429 (error 1015) for a block, or a challenge page | Fewer requests; for rate limiting, “The Challenge Passage does not apply” |
| Skip | Nothing visible: the request continues | Nothing to do |
Every challenge page carries the response header cf-mitigated: challenge, which is the reliable
way to tell a challenge from a block. This function sorts a response into the rows above:
import requests
def waf_verdict(url: str) -> str: """What a Cloudflare WAF did with one request, from the response alone.""" response = requests.get(url, timeout=30) if response.headers.get("cf-mitigated") == "challenge": return "challenged: pass the challenge once, then reuse cf_clearance" if response.status_code == 429: return "rate limited: slow down, a clearance does not help" if response.status_code == 403: return "refused: a 1xxx code in the body, such as 1020, is a Block only the owner can lift" return f"served: HTTP {response.status_code}"
print(waf_verdict("https://shop.example.com/"))The 5-second challenge and “Just a moment…” and “Checking your browser” articles cover the challenge page itself, and Cloudflare WAF rules explained has every action, the plan limits and the order the rules run in.
Passing a WAF challenge with one request
A challenge page is passed by a browser that runs Cloudflare’s checks, from one address, with one user agent. The cookie it earns is tied, in Cloudflare’s words, to “the specific visitor and device it was issued to”, which in practice means that address and that user agent. So a challenge is solved through your own proxy, and you use the result through the same proxy.
ZeroCaptcha’s CloudflareChallengeTask does this: you send the page and your proxy, and the task returns the cf_clearance cookie with the user agent it was issued for. The whole request, from the shell, with every field a real integration sends:
#!/usr/bin/env bash# Pass a Cloudflare WAF challenge on a site you may automate, then fetch the page with the clearance.set -euo pipefailAPI=${ZEROCAPTCHA_API:-https://api.zerocaptcha.io}KEY=${ZEROCAPTCHA_KEY:?Set ZEROCAPTCHA_KEY to your API key, zc_live_..., from the dashboard.}PROXY=${PROXY_URL:?Set PROXY_URL to your proxy, such as http://user:pass@proxy.example.net:8080.}PAGE=https://shop.example.com/
# 1. Create the task: the page behind the challenge (websiteURL), your proxy, where to POST the# result when the task ends (callbackUrl), and an Idempotency-Key, so a retried request returns# the same task instead of a second, paid one. A challenge page has no sitekey, so a challenge# task takes no websiteKey.TASK_ID=$(curl -sS --fail-with-body "$API/v1/tasks" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d "{\"type\": \"CloudflareChallengeTask\", \"websiteURL\": \"$PAGE\", \"proxy\": \"$PROXY\", \"callbackUrl\": \"https://example.com/zerocaptcha/callback\"}" | jq -r .id)
# 2. Read the task every 2 seconds until it ends, or wait for the callback instead.while :; do TASK=$(curl -sS "$API/v1/tasks/$TASK_ID" -H "Authorization: Bearer $KEY") STATUS=$(jq -r .status <<<"$TASK") if [ "$STATUS" != queued ] && [ "$STATUS" != running ]; then break; fi sleep 2doneCLEARANCE=$(jq -r '.solution.cookie.value // empty' <<<"$TASK")USER_AGENT=$(jq -r '.solution.userAgent // empty' <<<"$TASK")if [ -z "$CLEARANCE" ]; then jq -r '"\(.errorCode): \(.errorDescription)"' <<<"$TASK" >&2 exit 1fi
# 3. Fetch the page with the cookie and exactly its user agent, through the same proxy.curl -sS -o /dev/null -w '%{http_code}\n' "$PAGE" \ --proxy "$PROXY" \ -H "User-Agent: $USER_AGENT" \ -H "Cookie: cf_clearance=$CLEARANCE"Reuse the cookie for every request until the site challenges you again, then create a new task: one task per session, not one per request. The price per 1,000 solved tasks is on the pricing page; a task that fails or expires costs nothing, and there is no free tier. The same request in Node, Python, Go and PHP, and the createTask format, are in the Cloudflare WAF and 5-second challenges docs.
What a solver cannot do
- Lift a Block. A clearance passes challenges, at its level. Cloudflare’s clearance docs say nothing of lifting a Block, and a Block rule matches whether or not you hold a cookie. Error 1020 needs the owner.
- Skip the proxy. A clearance earned from a solver’s address is refused from yours, so there is
no proxyless challenge task:
CloudflareChallengeTaskProxylessis refused before anything is held. - Fix a mismatched client. A client whose TLS handshake does not look like the browser its user agent names may be challenged again despite a valid cookie, since Cloudflare’s bot detection reads TLS fingerprints. Use a browser, or a client that impersonates one; see TLS fingerprints and Cloudflare.
- Other vendors’ WAFs. ZeroCaptcha solves Cloudflare’s challenges only, not AWS WAF, Akamai or others; see WAF challenges across vendors.
If you run a feed for a partner, ask them to exempt your traffic instead: an IP Access rule that allows an address “will bypass any configured custom rules, rate limiting rules, WAF Managed Rules, and firewall rules”, which no solver can match for reliability. The Cloudflare WAF and 5-second challenge solver page has the rest of the task’s details.
Sources
- Cloudflare: Rules language actions (checked 1 October 2026)
- Cloudflare: WAF custom rules and rate limiting rules (checked 1 October 2026)
- Cloudflare: Clearance and Challenge Passage (checked 1 October 2026)
- Cloudflare challenges: detect a challenge response (checked 1 October 2026)
- Cloudflare: IP Access rules (checked 1 October 2026)
- Cloudflare: JA3 and JA4 fingerprints (checked 1 October 2026)
- ZeroCaptcha: Cloudflare WAF and 5-second challenges, for the task’s fields and the proxy rule
The team that builds and runs the ZeroCaptcha API. Articles are drafted with AI tools, then checked against the API's code and the primary sources each one cites.