Skip to content

Explainer

Cloudflare's Challenge Platform and cf_chl Parameters Explained

What /cdn-cgi/challenge-platform, window._cf_chl_opt, the cf_chl cookies and the __cf_chl_ URL parameters are, and what an automated client should do.

By 6 min readPublished

The Cloudflare challenge platform is the machinery behind Cloudflare’s challenge pages (“Just a moment…”) and its JavaScript detections. Its scripts are served from /cdn-cgi/challenge-platform/ on the protected site’s own hostname, a challenge page carries its settings in a window._cf_chl_opt object, the response carries the header cf-mitigated: challenge, and a passed challenge ends with a cf_clearance cookie. The cf_chl_* cookies and the __cf_chl_ URL parameters you may see along the way are internal to Cloudflare and undocumented; the only piece a client can build on is cf_clearance.

This explainer takes each visible piece in turn, with what Cloudflare documents about it, as checked on 1 October 2026. Automate only sites you are allowed to: see responsible captcha automation.

The pieces of a challenge response

When a WAF rule, Bot Management or a rate limiting rule decides to challenge a request, Cloudflare answers with an interstitial page instead of the site’s content. Here is what an HTTP client sees:

Piece What it is Documented?
Status 403 Cloudflare lists “WAF Custom or Managed Rules with challenge or block actions” among its causes of a 403 Yes
cf-mitigated: challenge header Set on every challenge page; “challenge” is its only value Yes
Content-Type: text/html “All challenge responses have text/html as the content-type, even if you requested a different resource type” Yes
Scripts under /cdn-cgi/challenge-platform/ The challenge’s code, served from the site’s own hostname Yes, as a path to allow
window._cf_chl_opt The challenge page’s settings object; Cloudflare sets cTplC: 1 in it when a custom template is in use Partly
Cookies cf_chl_rc_i, cf_chl_rc_ni, cf_chl_rc_m “for internal use which allows Cloudflare to identify production issues on clients” Yes, as internal
Query parameters beginning __cf_chl_ Added to URLs by the challenge flow No
cf_clearance cookie Set when the visitor passes; proves the visitor passed Yes

The first scripts load from the site’s own origin, but the check itself also needs challenges.cloudflare.com, the domain the Cloudflare Turnstile widget loads from too. Cloudflare says that “Challenge Pages are issued through the Cloudflare Challenge Platform, which uses the same underlying technology as Turnstile.” When a blocker or a network filter stops that domain, the page asks you to unblock it (see Please unblock challenges.cloudflare.com).

/cdn-cgi/challenge-platform

Cloudflare names the path in two places. Its JavaScript detections are injected as a script whose path follows the pattern /cdn-cgi/challenge-platform/…, only “in response to requests for HTML pages or page views, excluding AJAX calls”, and their result is stored in cf_clearance for 15 minutes. And its guidance for custom challenge pages says: “Do not block /cdn-cgi/challenge-platform/” through the page’s Content Security Policy.

For a site owner, that means allowing the site’s own origin in script-src, using nonces for inline scripts as the JavaScript detections page asks, and keeping Transform Rules that rewrite response headers off challenge responses (the rule expression is below). A Cloudflare Turnstile widget needs https://challenges.cloudflare.com in script-src and frame-src as well, and pre-clearance mode needs 'self' in connect-src for its calls to /cdn-cgi/.

For an automated client, /cdn-cgi/challenge-platform/ requests in a browser’s network log are a sign that a challenge or JavaScript detections ran on that page view. They are not an API to call: the scripts’ URLs and payloads are undocumented and change.

window._cf_chl_opt

A challenge page defines a JavaScript object named _cf_chl_opt on window. Cloudflare mentions it once, in its custom challenge page requirements: it “sets cTplC: 1 in window._cf_chl_opt when custom templates are active”. Nothing else about its fields is documented. In a browser you drive, its presence is a quick check that you are looking at a challenge page rather than the site:

// Run in the page, for example with Playwright's page.evaluate().
() => ({
challengePage: typeof window._cf_chl_opt === "object",
title: document.title,
});

The header check below is better, because it needs no browser and no guess about page internals.

The _cf_chl URL parameters

While a challenge runs, the address bar can show query parameters whose names begin with __cf_chl_. You may find such URLs in logs, in links people copy from their browser, or even in search results for Cloudflare’s own documentation. Cloudflare’s documentation, checked on 1 October 2026, does not describe them. What follows is our advice, not Cloudflare’s:

  • Strip them before you store or queue a URL. They make the same page look like many different URLs, which fills a crawler’s queue with duplicates.
  • Don’t treat them as a pass. The documented proof of a passed challenge is the cf_clearance cookie. Nothing in the documentation says a URL parameter grants access, so don’t build on one.
  • Don’t replay them. They belong to one visitor’s challenge.

This Python helper removes them and keeps every other parameter:

from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
def strip_challenge_params(url):
parts = urlsplit(url)
query = [
(name, value)
for name, value in parse_qsl(parts.query, keep_blank_values=True)
if not name.startswith("__cf_chl_")
]
return urlunsplit(parts._replace(query=urlencode(query)))
print(strip_challenge_params("https://shop.example.com/item?id=7&__cf_chl_example=abc"))

The cf_chl cookies and cf_clearance

The cf_chl_rc_i, cf_chl_rc_ni and cf_chl_rc_m cookies are, in Cloudflare’s words, “for internal use which allows Cloudflare to identify production issues on clients”. Cloudflare sets the Partitioned attribute on them and on cf_clearance, and cf_clearance also carries SameSite=None; Secure, so “a clearance obtained in one top-level context is not reused in a different top-level context”.

cf_clearance is the result. It “proves to Cloudflare that the visitor is a verified human and has passed Cloudflare’s client-side verifications”, it lasts for the zone’s Challenge Passage, and it is tied to the visitor and device that earned it: in practice the IP address and user agent. The cf_clearance cookie explained covers how to use one, and the __cf_bm cookie the bot-detection cookie beside it.

Detect a challenge in code

Cloudflare documents one reliable signal: the cf-mitigated header. Its own example is JavaScript:

const response = await fetch("https://shop.example.com/api/items");
if (response.headers.get("cf-mitigated") === "challenge") {
console.log("Cloudflare challenged this request; it needs a cf_clearance cookie.");
} else {
console.log(response.status);
}

A site owner can also match challenge responses in rules: Cloudflare’s example for keeping Transform Rules off challenge pages is not cf.response.error_type in {"managed_challenge" "iuam" "legacy_challenge" "country_challenge"}, which names the kinds of challenge response the field reports (iuam is Under Attack mode).

Getting past it, where you are allowed to

A challenge page is passed by a browser that runs its scripts, or by a service that does so for you. ZeroCaptcha’s challenge task passes the page through your own proxy and returns the cf_clearance cookie with the user agent it is bound to, which your client then sends through the same proxy. See Cloudflare WAF and 5-second challenges and the Cloudflare WAF and 5-second challenge solver. If a page keeps coming back after you pass it, the challenge loop article lists the causes.

Sources

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.

Questions

What is /cdn-cgi/challenge-platform?

It is the path on a Cloudflare-proxied site's own hostname from which Cloudflare serves the scripts of its challenge platform: challenge pages and JavaScript detections. A site's Content Security Policy must not block it.

What are the __cf_chl_ parameters in a URL?

They are query parameters Cloudflare's challenge platform can add to a URL while a challenge runs. Cloudflare's documentation does not describe them, so treat them as internal: strip them from URLs you store, and rely on the cf_clearance cookie, which is the documented proof of a passed challenge.

How do I detect a Cloudflare challenge page in code?

Check the cf-mitigated response header. Cloudflare sets it to challenge on every challenge page, and the response's content type is text/html whatever you asked for.

Read next

This article is part of the Cloudflare WAF and 5-second challenge solver hub. Every task is charged only when a token is ready.

Get an API key