Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Curl Impersonate Php Laravel Package

kelvinzer0/curl-impersonate-php

PHP wrapper for curl-impersonate that makes HTTP requests mimic real browser TLS and HTTP/2 fingerprints, helping bypass bot detection and anti-scraping checks while keeping the familiar cURL-style API.

Deep Wiki
Context7

Getting Started

The package is a thin PHP wrapper around a patched curl build (curl-impersonate). The PHP code is the easy part — the real dependency is the native binary. Budget most of your first hour for that.

  1. Install the native layer first. kelvinzer0/curl-impersonate-php does not ship the impersonating curl. You need the curl-impersonate-chrome / curl-impersonate-firefox binaries present on the host (or a Docker base image that includes them). Confirm with curl-impersonate-chrome --version and curl-impersonate-chrome -V | grep -i nghttp2.
  2. Then Composer:
    composer require kelvinzer0/curl-impersonate-php
    
  3. Read in this order: the README's Requirements/Installation section (binary path handling is the usual source of pain), then the main client class to see the real method surface — wrappers in this space vary between get()/post(), a Guzzle-style client, or a fluent builder, so confirm before writing code around it.
  4. First use case: a URL that returns 403 with a Cloudflare/Akamai page to vanilla curl or Guzzle. Fetch it with the wrapper's Chrome profile and confirm you now get the real HTML, plus cf-ray/server headers instead of a challenge page.

A typical shape (verify names against the source — this family of packages is not consistent):

$client = new CurlImpersonate('chrome'); // profile / binary selector
$client->setHeaders(['Accept-Language' => 'en-US,en;q=0.9']);
$response = $client->get('https://target.example/protected');

Validate immediately against a fingerprint echo service (https://tls.peet.ws/api/all or tls.browserleaks.com/json). If the JA3/JA4 there matches stock curl instead of Chrome, your binary path is wrong and everything downstream is a false sense of security.

Implementation Patterns

Bind it into the container. Wrap the client in a small ImpersonatedHttpClient service and register it in a service provider. Don't instantiate the wrapper in controllers — you want one place to control profile selection, proxy, and timeouts, and one place to swap in a fake during tests.

Match the whole profile, not just TLS. The point is consistency: TLS ClientHello + HTTP/2 SETTINGS/priority frames + header order + User-Agent. Pick a profile (chrome, chrome110, firefox, safari) and derive the UA and Accept* headers from that profile's real browser version. Hard-coding a random modern UA on top of an older Chrome fingerprint is worse than doing nothing.

Cookie jar per session. Multi-step flows (landing page → CSRF token → login POST → authenticated fetch) need a persistent cookie jar on the same client instance. Reuse the instance within a job; create a fresh one per account to avoid cross-contamination.

Pair with proxies, and treat IP reputation as the primary variable. For most anti-bot stacks today, fingerprint gets you past the handshake and IP/ASN reputation determines whether you see a challenge. Rotate proxies per request or per session depending on whether the flow is stateful, and keep the proxy sticky for login sequences.

Queue it and rate-limit per domain. These calls are slow (hundreds of ms to seconds) and get you banned fast if bursty. A Laravel job per URL with RateLimiter::for('target-domain') and a release() backoff on 429/403 is the standard pattern.

Classify failures explicitly. Distinguish:

  • 403 with a JS challenge body → fingerprint/IP insufficient, escalate to a headless browser.
  • 429 / 503 with Retry-After → back off, don't rotate aggressively.
  • 200 but empty/blocked content → you're being soft-blocked; inspect the body, don't trust the status code.

Build this into a small result object (ok, challenged, blocked, error) so downstream jobs can act on it.

Keep curl-impersonate as one tier in a ladder. Pattern that works: plain Guzzle → curl-impersonate → headless browser (Playwright/Puppeteer) → manual/paid unblocker. Route up the ladder only when the cheaper tier fails, and cache which tier worked per domain.

Testing. Wrap the client behind an interface and bind a fake in tests that returns recorded fixtures. You do not want CI depending on a native binary and live anti-bot endpoints.

Gotchas and Tips

  • The silent-failure trap. If the wrapper can't find the impersonating binary and falls back to system curl, requests still succeed — they just carry the wrong fingerprint. Always assert the fingerprint once in a smoke test; a green test suite proves nothing without it.
  • No nghttp2 = no HTTP/2 = wrong fingerprint. If the binary was built without HTTP/2, it negotiates HTTP/1.1 and the whole point is lost. Check curl-impersonate-chrome -V for nghttp2.
  • Version drift. Chrome profile versions age. Cloudflare and Datadome fingerprint new browser builds; a chrome110 profile in 2026 is a red flag on its own. Pin the binary version in your Dockerfile and schedule upgrades.
  • Docker is the sane deployment target. Compiling curl-impersonate on a shared host or a managed PHP runtime is generally not possible. Use a base image with the binaries, set the binary path via env var, and mount it into config.
  • It solves the handshake, not JavaScript. Turnstile, hCaptcha, and Datadome interstitials require executing JS. No amount of TLS spoofing fixes that — recognize the wall early and don't burn days tuning headers.
  • Header ordering matters. Some wrappers let you pass an associative array; PHP arrays preserve insertion order, so the order you set headers is the wire order. Set them in browser order (Host, Connection, sec-ch-ua, User-Agent, Accept, Accept-Language, ...) or use the profile's default and override sparingly.
  • Don't mix with Laravel's Http facade. Http::get() uses Guzzle and will silently bypass your impersonating client. Route all protected fetches through the wrapper's service class.
  • Timeouts and connection reuse. Set connect and total timeouts explicitly; the binary can hang longer than Guzzle defaults. If the wrapper exposes handle reuse, prefer reusing within a session — but be aware a stale handle after a proxy rotation is a common bug.
  • Log the evidence. Persist status, server, cf-ray, response size, and a hash of the first 1KB of body for failures. Anti-bot behavior changes quietly; without this you can't tell when a profile stopped working.
  • Legal/ToS check before scale. Scraping protected endpoints is a policy decision as much as a technical one; get it signed off, and respect robots.txt and rate limits where they apply.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route