Cloudflare bypass tool using Playwright for curl requests
Project description
CurlWright
Execute curl requests through a real Playwright browser when anti-bot protection gets in the way
Overview
CurlWright is a Python tool that takes a curl command, opens a real Google Chrome browser through Patchright (a stealth-patched Playwright), works through Cloudflare and similar browser-side friction, and returns the final HTTP response in a form that still feels close to curl-driven workflows.
It is useful when a plain HTTP client is not enough because the target requires browser execution, JavaScript, cookies, challenge handling, or a persisted trusted session.
On the Cloudflare bypass: CurlWright drives real Google Chrome (
channel="chrome") with Patchright's protocol-level stealth (navigator.webdriverhidden, no CDPRuntime.enableleak), so it transparently handles passive Cloudflare (CDN) and has a real shot at managed / "I'm Under Attack" / non-interactive challenges.Run headed for the best results. The default (no
--headless/--no-gui) launches a visible Chrome with a genuine, consistent fingerprint — the strongest stealth.--no-gui/--headlessuse Chrome's new headless for servers/CI, but the browser still advertisesHeadlessChromein its user agent, which strict bot-management may flag; on a VPS, run underxvfbfor headed-quality stealth.Hardened managed challenges →
--engine nodriver. The default Patchright engine clears passive CF and Turnstile, but the toughest "Just a moment" managed challenges fingerprint Playwright's automation-protocol signature (Runtime.enable/Target.setAutoAttach) and stall at HTTP 403. Pass--engine nodriverto drive Chrome over a plain CDP WebSocket (no such signature), which clears those interstitials. nodriver requires headed mode (omit--headless); headless is detected just like Patchright. A flagged/datacenter IP still gets blocked regardless of engine — use a residential proxy via curl-x.It is not a guaranteed solver for every hardened anti-bot setup — some still require a paid CAPTCHA service, which CurlWright does not bundle. It detects the challenge type and progresses what it can; when a page cannot be cleared it fails loudly with diagnostics (exit code 10).
Key Features
| Feature | Description |
|---|---|
| Browser-backed curl execution | Parse a curl command and execute it through real Chrome (Patchright) |
| Cloudflare challenge handling | Detect and progress browser-side verification; clears passive CF and many managed/non-interactive challenges (not a guaranteed interactive-Turnstile solver) |
| Turnstile detection | Detects Turnstile flows and attempts non-interactive resolution |
| Trusted session reuse | Persist per-domain trust state and cookies between runs |
| JSON and SARIF outputs | Machine-readable output for automation and CI/security tooling |
| Headless and server mode | Works in local desktop mode or with --no-gui in CI/VPS environments |
| Python library mode | Use as a CLI or from Python code |
| Clean layered architecture | Explicit domain, application, infrastructure, and interfaces layers |
Supported curl Inputs
Methods -X/--request, -I/--head, -G/--get
Headers -H/--header
Body -d/--data, --data-raw, --data-binary, --data-urlencode
Cookies -b/--cookie
Auth -u/--user
Network -x/--proxy, -L/--location, -k/--insecure, --max-time
Input Forms Direct command (-c) or file (-f)
Installation
From PyPI (Recommended)
pip install curlwright
# CurlWright drives your system Google Chrome (channel="chrome").
# If you do not have Chrome installed, fetch a Chrome build for Patchright:
patchright install chrome
Requires Google Chrome on the host (the default
channel="chrome"). On a server/VPS, install Chrome (or runpatchright install chrome); headless runs use Chrome's new headless mode.
From Source
git clone https://github.com/seifreed/Curlwright.git
cd Curlwright
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -e ".[dev]"
patchright install chrome # only if system Chrome is absent
Quick Start
# Execute a curl command directly
curlwright -c "curl https://example.com"
# Read the curl command from a file
curlwright -f request.txt
# Emit structured JSON for automation
curlwright -f request.txt --json-output
Usage
Command Line Interface
# Basic request
curlwright -c "curl https://httpbin.org/get"
# Save the response body to a file
curlwright -f request.txt -o response.html
# Server/CI mode
curlwright -f request.txt --no-gui --json-output
# Persist diagnostics for failures
curlwright -f request.txt --artifact-dir .artifacts/run-1 --sarif-output report.sarif
# Increase retries and timeout
curlwright -c "curl https://target.example" --timeout 60 --retries 5 --delay 3
Available Options
| Option | Description |
|---|---|
-c, --curl |
Curl command to execute |
-f, --file |
File containing the curl command |
--engine |
Browser engine: patchright (default) or nodriver (clears hardened managed challenges; headed only) |
--headless |
Run Chromium headless |
--fast |
Skip the stealth warm-up for non-protected targets (~20% faster; less stealthy, challenge path unchanged) |
--no-gui |
Server-oriented mode without display requirements |
--user-agent |
Override the browser user agent |
--timeout |
Request timeout in seconds |
--cookie-file |
Override cookie persistence path |
--state-file |
Override trusted-session state path |
--artifact-dir |
Directory for diagnostics, screenshots, HTML and logs |
--profile-dir |
Override the persistent Chromium profile directory |
--no-persist-cookies |
Disable automatic cookie load/save |
--import-cookies |
Load cookies from a JSON file into the jar before the request |
--export-cookies |
Write the captured session cookies to a JSON file after the request |
--clear-cookies |
Clear the persistent cookie jar before the request |
--bypass-attempts |
Challenge-resolution attempts per request |
--retries |
Retry count after failures |
--delay |
Delay between retries |
-o, --output |
Save the rendered response output to a file |
-v, --verbose |
Print a runtime execution summary |
--json-output |
Emit the stable JSON contract |
--sarif-output |
Write a SARIF 2.1.0 report |
Output Contracts
--json-output emits a stable machine-readable structure:
{
"schema_version": 1,
"kind": "curlwright-result",
"ok": true,
"exit_code": 0,
"response": {
"status": 200,
"url": "https://example.com/",
"headers": {},
"body": "..."
},
"meta": {}
}
Failure JSON includes error, error_type, exit_code, and for bypass failures also artifact_dir plus an assessment block.
Python Library
Basic Usage
import asyncio
from curlwright import RequestExecutor
async def main() -> None:
executor = RequestExecutor(headless=True, timeout=30)
result = await executor.execute('curl -H "Accept: application/json" https://httpbin.org/get')
print(result["status"])
print(result["url"])
print(result["body"][:120])
await executor.close()
asyncio.run(main())
Parse curl Before Execution
from curlwright import CurlParser
parser = CurlParser()
request = parser.parse('curl -X POST -H "Content-Type: application/json" https://api.example.com')
print(request.method)
print(request.url)
print(request.headers)
Cookie Persistence
import asyncio
from curlwright import RequestExecutor
from curlwright.utils import CookieManager
async def main() -> None:
cookies = CookieManager("cookies.json")
executor = RequestExecutor(headless=True, cookie_file="cookies.json")
result = await executor.execute("curl https://example.com")
print(result["status"])
await executor.close()
asyncio.run(main())
Examples
API Request Through A Browser Session
curlwright -c 'curl -H "Authorization: Bearer TOKEN" https://api.example.com/data' --json-output
Request File
Create request.txt:
curl -X GET \
-H "Accept: application/json" \
-H "User-Agent: Analyst/1.0" \
-b "session=abc123" \
https://protected.example.com/api/data
Run it:
curlwright -f request.txt -o response.json
CI-Friendly Execution
mkdir -p .artifacts/job-1
curlwright \
-f request.txt \
--no-gui \
--json-output \
--sarif-output .artifacts/job-1/curlwright.sarif \
--artifact-dir .artifacts/job-1 \
--state-file .artifacts/job-1/state.json \
--cookie-file .artifacts/job-1/cookies.pkl \
> .artifacts/job-1/result.json
Headless Retry-Tuned Request
curlwright -c "curl https://target.example" --headless --timeout 90 --retries 5 --delay 2
Architecture
The active codebase follows an explicit layered structure:
curlwright/
domain/ Pure models, policies and ports
application/ Use cases and orchestration
infrastructure/ Playwright, persistence and parser adapters
interfaces/ CLI, JSON and SARIF presenters
bootstrap.py Composition root
This separation keeps browser automation and heuristics in infrastructure while the policy and use-case flow stay isolated from Playwright details.
Requirements
- Python
>=3.13,<3.15 - Playwright Chromium installed via
python -m playwright install chromium - See
pyproject.tomlfor the full package metadata and dependency declarations
Contributing
Contributions are welcome. If you want to change behavior, add support for more curl flags, or improve challenge handling, open an issue or send a pull request.
- Fork the repository
- Create your branch:
git checkout -b feature/my-change - Run the test suite
- Commit your changes
- Push the branch
- Open a pull request
Support the Project
If CurlWright is useful in your workflows, you can support the project here:
License
This project is licensed under the MIT License. See LICENSE for details.
Attribution
- Author: Marc Rivero | @seifreed
- Repository: github.com/seifreed/Curlwright
Built for browser-backed automation and resilient protected-request workflows
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file curlwright-3.0.0.tar.gz.
File metadata
- Download URL: curlwright-3.0.0.tar.gz
- Upload date:
- Size: 75.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a546a22ec4b607f97a0f86d8bb9cc0e34d053bc23d143ba9c3875431c08a792
|
|
| MD5 |
9f743fe26fe585063366c4de23bd9aff
|
|
| BLAKE2b-256 |
b13837d9bd85c12a356e9f13027f70c60af530620a7e9ebaddd84380d9c320c0
|
Provenance
The following attestation bundles were made for curlwright-3.0.0.tar.gz:
Publisher:
publish.yml on seifreed/Curlwright
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
curlwright-3.0.0.tar.gz -
Subject digest:
9a546a22ec4b607f97a0f86d8bb9cc0e34d053bc23d143ba9c3875431c08a792 - Sigstore transparency entry: 1994301564
- Sigstore integration time:
-
Permalink:
seifreed/Curlwright@d7779550bcd8c38f2292fe556f3e80217778b2bc -
Branch / Tag:
refs/tags/v3.0.0 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d7779550bcd8c38f2292fe556f3e80217778b2bc -
Trigger Event:
push
-
Statement type:
File details
Details for the file curlwright-3.0.0-py3-none-any.whl.
File metadata
- Download URL: curlwright-3.0.0-py3-none-any.whl
- Upload date:
- Size: 56.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f74a31b79a3f61829c77121f00f7a4d69267ef7ba250924845c754c77c8dc683
|
|
| MD5 |
2380e94cb303f2a3d26a89dfa569ca0d
|
|
| BLAKE2b-256 |
29733fbf18c17de4a6b4afc932227040738eeeb97cb5784f68de583313b9ce51
|
Provenance
The following attestation bundles were made for curlwright-3.0.0-py3-none-any.whl:
Publisher:
publish.yml on seifreed/Curlwright
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
curlwright-3.0.0-py3-none-any.whl -
Subject digest:
f74a31b79a3f61829c77121f00f7a4d69267ef7ba250924845c754c77c8dc683 - Sigstore transparency entry: 1994301737
- Sigstore integration time:
-
Permalink:
seifreed/Curlwright@d7779550bcd8c38f2292fe556f3e80217778b2bc -
Branch / Tag:
refs/tags/v3.0.0 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d7779550bcd8c38f2292fe556f3e80217778b2bc -
Trigger Event:
push
-
Statement type: