← Back to list

PHP cURL for Web Scraping: Sessions, Cookies, and Headers

How to handle cookies, CSRF tokens, redirects, and Chrome-matching headers in PHP. Same task in raw cURL and Guzzle side by side

HasData · 2026-04-09 07:36 · 0 claps · 2.1 min read
#php #web-scraping #curl #cookies #http-request
Open on Medium ↗
Wiki topics: ML · Machine Learning

PHP cURL for Web Scraping: Sessions, Cookies, and Headers

The difference between a scraper that works on day one and one that still works a month later comes down to how you handle cookies, redirects, and headers. I show each concept in raw cURL and in Guzzle so you can see where the cutoff is for your project.

Basic Page Fetch: cURL vs Guzzle

cURL with minimum browser headers:

$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL            => 'https://news.ycombinator.com/',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_MAXREDIRS      => 5,
    CURLOPT_TIMEOUT        => 15,
    CURLOPT_HTTPHEADER     => [
        'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Accept: text/html,application/xhtml+xml,application/xml;q=0.9',
        'Accept-Language: en-US,en;q=0.9',
    ],
]);

$html = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

Same thing in Guzzle:

$client = new Client([
    'timeout'         => 15,
    'allow_redirects' => ['max' => 5],
    'headers'         => [
        'User-Agent'      => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Accept'          => 'text/html,application/xhtml+xml,application/xml;q=0.9',
        'Accept-Language'  => 'en-US,en;q=0.9',
    ],
]);

$response = $client->get('https://news.ycombinator.com/');

Identical output. The difference shows up when you need cookies, retries, or concurrent requests. Guzzle scales, cURL’s procedural API doesn’t.

Cookie Handling and Session Persistence

Most sites track state through cookies. In cURL, you manage them by pointing to a file:

$cookieFile = tempnam(sys_get_temp_dir(), 'scraper_cookies_');

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL            => 'https://quotes.toscrape.com/login',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEJAR      => $cookieFile,
    CURLOPT_COOKIEFILE     => $cookieFile,
]);

$html = curl_exec($ch);
preg_match('/name="csrf_token" value="(.+?)"/', $html, $matches);

curl_setopt_array($ch, [
    CURLOPT_URL        => 'https://quotes.toscrape.com/login',
    CURLOPT_POST       => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'csrf_token' => $matches[1],
        'username'   => 'admin',
        'password'   => 'admin',
    ]),
]);

$html = curl_exec($ch);
curl_close($ch);
unlink($cookieFile);

Guzzle’s CookieJar keeps everything in memory:

$jar = new CookieJar();
$client = new Client(['cookies' => $jar]);

$response = $client->get('https://quotes.toscrape.com/login');
$crawler = new Crawler($response->getBody()->getContents());
$csrfToken = $crawler->filter('input[name="csrf_token"]')->attr('value');

$client->post('https://quotes.toscrape.com/login', [
    'form_params' => [
        'csrf_token' => $csrfToken,
        'username'   => 'admin',
        'password'   => 'admin',
    ],
]);
// Every subsequent request carries the authenticated session

Notice the CSRF extraction uses DomCrawler’s filter() instead of a fragile regex. Use the parser, not string matching.

Headers That Pass Inspection

Bare-minimum headers get you blocked on most serious sites. Anti-bot systems check whether your header combination is consistent with a real browser. A Chrome User-Agent with missing sec-ch-ua headers is an instant red flag.

Headers that match what Chrome 146 actually sends:

$client = new Client([
    'headers' => [
        'User-Agent'         => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
        'Accept'             => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
        'Accept-Language'    => 'en-US,en;q=0.9',
        'Accept-Encoding'    => 'gzip, deflate, br',
        'sec-ch-ua'          => '"Chromium";v="146", "Google Chrome";v="146", "Not-A.Brand";v="24"',
        'sec-ch-ua-mobile'   => '?0',
        'sec-ch-ua-platform' => '"Windows"',
        'Sec-Fetch-Dest'     => 'document',
        'Sec-Fetch-Mode'     => 'navigate',
        'Sec-Fetch-Site'     => 'none',
        'Sec-Fetch-User'     => '?1',
    ],
]);

Your headers should tell a consistent story. If the User-Agent says Chrome on Windows, every other header should match what Chrome on Windows actually sends.

The full guide covers redirect handling, proxy rotation, and anti-bot evasion with complete code: PHP Web Scraping: The Complete Guide.


메타데이터
post_id
7fdbbae5abfb
slug
php-curl-for-web-scraping-sessions-cookies-and-headers-7fdbbae5abfb
url
https://medium.com/@hasdata/php-curl-for-web-scraping-sessions-cookies-and-headers-7fdbbae5abfb
canonical_url
https://medium.com/@hasdata/php-curl-for-web-scraping-sessions-cookies-and-headers-7fdbbae5abfb
author_url
https://medium.com/@hasdata
status
ok
fetched_at
2026-06-15 20:49:13