# Playwright and Cloudflare "Just a Moment": Using cf_clearance

> Why Playwright gets stuck on Cloudflare's "Just a moment..." page, how to detect it, and how to load a cf_clearance cookie with its user agent and proxy.

- Source: https://zerocaptcha.io/blog/playwright-cloudflare-challenge
- Published: 2026-09-30
- Updated: 2026-10-01
- Author: ZeroCaptcha Engineering

When Playwright opens a site and stays on "Just a moment...", it has met a Cloudflare challenge
page: a rule on the site asks every visitor to pass Cloudflare's check before the page loads.
Cloudflare documents that browser automation frameworks, "such as Selenium, Puppeteer,
Playwright, and Cypress, are not supported for solving production challenges", so waiting often
does not help. What the page wants is a `cf_clearance` cookie, which a browser earns by passing the
challenge. In Playwright you can use one: create a context with the cookie's user agent, add the
cookie, and browse through the same proxy that earned it. This tutorial shows how to detect the
challenge and load a clearance, on sites you are allowed to automate.

## Detect the challenge

Cloudflare's docs say a challenge response carries the header `cf-mitigated: challenge`, whatever
resource was requested. Checking it is more reliable than looking for the title:

```js
import { chromium } from "playwright";

const browser = await chromium.launch();
const page = await browser.newPage();
const response = await page.goto("https://shop.example.com/", { waitUntil: "domcontentloaded" });
if (response?.headers()["cf-mitigated"] === "challenge") {
  console.log(`Cloudflare challenge, HTTP ${response.status()}: ${await page.title()}`);
}
await browser.close();
```

A challenge usually answers with HTTP 403 and the title "Just a moment...". An error page with a
code such as 1020 is different: a rule blocked the request, and no cookie helps. See
[Cloudflare error 1020](https://zerocaptcha.io/blog/cloudflare-error-1020).

## Why the clearance needs its user agent and proxy

Passing a challenge sets `cf_clearance`, which Cloudflare says "proves to Cloudflare that the
visitor is a verified human" and "is securely tied to the specific visitor and device it was issued
to". Its lifetime is the site's Challenge Passage setting, 30 minutes by default. In practice that
means three things for Playwright:

1. **The same user agent.** Set the context's `userAgent` to exactly the one the cookie was issued
   with.
2. **The same network.** Cloudflare notes that a Managed Challenge solved from a different IP than
   the one it was issued to is not valid, so browse through the proxy that earned the clearance.
3. **A browser that matches the user agent.** A user agent string says which browser it is;
   Chromium's own behavior should agree. Use a Chromium whose major version is close to the one in
   the user agent. A mismatch is a signal Cloudflare may act on, and it will challenge again.

## Get a clearance and load it

ZeroCaptcha's challenge task passes the page through your proxy and returns the cf_clearance cookie with the user agent it was issued for.
The task takes the page's URL and your proxy; a challenge page has no sitekey. The compatible
format answers the way CapSolver's `AntiCloudflareTask` does, with `solution.userAgent` and
`solution.cookies.cf_clearance`. The [challenge pages docs](https://zerocaptcha.io/docs/challenges) list every field.

Save this as `clearance.mjs`, with `playwright` installed and `ZEROCAPTCHA_API`,
`ZEROCAPTCHA_KEY` and `PROXY_URL` (such as `http://user:pass@proxy.example.net:8080`) set:

```js
import { chromium } from "playwright";

const API = process.env.ZEROCAPTCHA_API;
const KEY = process.env.ZEROCAPTCHA_KEY;
const PROXY_URL = process.env.PROXY_URL;
const TARGET = "https://shop.example.com/";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function call(method, body) {
  const response = await fetch(`${API}/${method}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ clientKey: KEY, ...body }),
    signal: AbortSignal.timeout(15_000),
  });
  const reply = await response.json();
  if (reply.errorId) throw new Error(`${method}: ${reply.errorCode}`);
  return reply;
}

async function getClearance(websiteURL, proxy) {
  const { taskId } = await call("createTask", { task: { type: "CloudflareChallengeTask", websiteURL, proxy } });
  const deadline = Date.now() + 180_000;
  while (Date.now() < deadline) {
    await sleep(2_000);
    const result = await call("getTaskResult", { taskId });
    if (result.status === "ready") {
      return { userAgent: result.solution.userAgent, cookie: result.solution.cookies.cf_clearance };
    }
  }
  throw new Error(`task ${taskId}: no clearance within 180 seconds`);
}

const { userAgent, cookie } = await getClearance(TARGET, PROXY_URL);

// Browse through the same proxy, with the same user agent, carrying the cookie.
const proxy = new URL(PROXY_URL);
const browser = await chromium.launch({
  proxy: {
    server: `${proxy.protocol}//${proxy.host}`,
    username: decodeURIComponent(proxy.username),
    password: decodeURIComponent(proxy.password),
  },
});
const context = await browser.newContext({ userAgent });
await context.addCookies([{ name: "cf_clearance", value: cookie, url: TARGET }]);
const page = await context.newPage();
const response = await page.goto(TARGET, { waitUntil: "domcontentloaded" });
console.log(response?.headers()["cf-mitigated"] === "challenge" ? "Challenged again" : await page.title());
await browser.close();
```

Run it with `node clearance.mjs`. A challenge that is not passed ends with
`ERROR_CAPTCHA_UNSOLVABLE`, and a proxy that resolves to a private address with
`ERROR_PROXY_NOT_ALLOWED`; neither costs anything. The [errors reference](https://zerocaptcha.io/docs/reference/errors)
lists every code.

## When the challenge comes back

- **After about 30 minutes:** the site's Challenge Passage ended. Get a new clearance.
- **At once:** the cookie is being sent from another IP, with another user agent, or by a browser
  whose other signals contradict the user agent. Check the proxy and the Chromium version.
- **On some requests only:** a CORS preflight (`OPTIONS`) does not carry the cookie, and Cloudflare
  lists rules features combined with challenges among the causes of loops. See
  [Cloudflare challenge loop](https://zerocaptcha.io/blog/cloudflare-challenge-loop).

The [cf_clearance cookie explained](https://zerocaptcha.io/guides/cf-clearance-cookie-explained) covers the cookie itself,
and the [Cloudflare WAF and 5-second challenge solver](https://zerocaptcha.io/cloudflare-challenge-solver) page the task type. For a
Cloudflare Turnstile widget inside a form, which needs a token rather than a cookie, see
[Playwright on the Cloudflare Turnstile solver page](https://zerocaptcha.io/cloudflare-turnstile-solver/playwright).
Automate only sites you are allowed to: see
[responsible captcha automation](https://zerocaptcha.io/guides/responsible-captcha-automation).

ZeroCaptcha is not affiliated with CapSolver; its name appears only to describe the reply format.

## Sources

- Cloudflare challenges: [supported browsers](https://developers.cloudflare.com/cloudflare-challenges/reference/supported-browsers/),
  [detect a challenge response](https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/detect-response/),
  [clearance](https://developers.cloudflare.com/cloudflare-challenges/concepts/clearance/),
  [Challenge Passage](https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/challenge-passage/),
  [how challenges work](https://developers.cloudflare.com/cloudflare-challenges/concepts/how-challenges-work/)
  and [troubleshooting](https://developers.cloudflare.com/cloudflare-challenges/troubleshooting/challenge-solve-issues/)
  (checked 1 October 2026).
- Playwright: [BrowserContext.addCookies](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-cookies)
  and [network proxies](https://playwright.dev/docs/network#http-proxy), version 1.63.0 (checked
  1 October 2026).

## Questions

### Why does Playwright get stuck on Cloudflare's "Just a moment..." page?

Cloudflare documents that browser automation frameworks, Playwright included, are not supported for solving production challenges, so an automated browser may never pass the page. The page is a Cloudflare challenge that wants a cf_clearance cookie.

### How do I use a cf_clearance cookie in Playwright?

Create a browser context with the user agent the cookie was issued for, add the cookie with context.addCookies, and browse through the same proxy that earned it. Cloudflare ties the cookie to the visitor and device it was issued to.

### How do I detect a Cloudflare challenge in Playwright?

Check the navigation response: a challenge page is served with a cf-mitigated header set to challenge, usually with HTTP 403 and the title "Just a moment...".
