PHP cURL Proxy Scraping: CURLOPT_PROXY, Auth en Guzzle

De meeste PHP proxy snippets online bevatten nog steeds dezelfde drie bugs uit 2011. Hier is een schone CURLOPT_PROXY setup — auth, SOCKS5, curl_multi rotatie, Guzzle en time-outs — die standhoudt in productie.

PHP cURL proxy code heeft een eigenaardig probleem: de hoogst gerangschikte snippets zijn een decennium oud en bevatten veel echte bugs — dubbele curl_exec() oproepen die elke aanvraag twee keer uitvoeren, CURLOPT_SSL_VERIFYPEER uitgeschakeld "om HTTPS te laten werken", opties ingesteld op waarden die al standaard waren. Deze gids is de huidige, correcte versie: CURLOPT_PROXY met authenticatie, SOCKS5, parallelle rotatie met curl_multi, dezelfde setup in Guzzle, en time-outs die voorkomen dat een worker de hele nacht blijft hangen.

Een minimaal, correct PHP cURL proxy voorbeeld

<?php
function fetch(string $url): string|false {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_PROXY          => 'http://gate.quantumproxies.io:8000',
        CURLOPT_PROXYUSERPWD   => 'USER:PASS',
        CURLOPT_RETURNTRANSFER => true,  // return the body, don't echo it
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 3,
        CURLOPT_CONNECTTIMEOUT => 10,    // seconds to reach the proxy
        CURLOPT_TIMEOUT        => 30,    // seconds for the whole transfer
        CURLOPT_ENCODING       => '',    // accept gzip/br, decode automatically
    ]);
    $body = curl_exec($ch);
    if ($body === false) {
        error_log('cURL error ' . curl_errno($ch) . ': ' . curl_error($ch));
    }
    curl_close($ch);
    return $body;
}

echo fetch('https://httpbin.org/ip'); // -> the proxy exit IP

Inloggegevens kunnen in CURLOPT_PROXYUSERPWD staan zoals getoond, of inline in de proxy-URL (http://USER:PASS@host:port) — beide werken. CURLOPT_RETURNTRANSFER is degene die beginners missen: zonder dit echoot curl_exec() de respons direct naar de output en retourneert true, wat nooit is wat een scraper wil. In PHP 8 is de handle een CurlHandle object in plaats van een resource; de optie-aanroepen blijven ongewijzigd.

De opties die mensen blindelings overnemen (en één echte beveiligingsfout)

Checklist van PHP cURL proxy opties om in te stellen versus opties om te vermijden, inclusief de SSL_VERIFYPEER fout
Zes opties dragen de hele klus; de rechterkolom is copy-paste schuld uit tutorials van 2011.

SOCKS5 proxies in PHP

<?php
// scheme style: socks5h = DNS resolved by the proxy (use for scraping)
curl_setopt($ch, CURLOPT_PROXY, 'socks5h://USER:PASS@gate.quantumproxies.io:1080');

// split style: same result via CURLOPT_PROXYTYPE
curl_setopt($ch, CURLOPT_PROXY, 'gate.quantumproxies.io:1080');
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'USER:PASS');

Het schema bepaalt waar DNS plaatsvindt: socks5:// (en CURLPROXY_SOCKS5) lost hostnamen op uw server op, waardoor elk doeldomein naar uw lokale resolver lekt; socks5h:// (CURLPROXY_SOCKS5_HOSTNAME) lost op bij de proxy-uitgang. Geef de voorkeur aan de laatste. QuantumProxies biedt HTTP en SOCKS5 in elk plan, dus het wisselen van protocollen is een wijziging van één regel, geen nieuw abonnement. Dezelfde DNS-regel geldt op de opdrachtregel — zie onze curl proxy recepten voor de shell-equivalenten van alles op deze pagina.

Rotatie en parallelle aanvragen met curl_multi

PHP heeft geen async/await, maar het heeft er geen nodig voor parallelle scraping — curl_multi voert tientallen overdrachten gelijktijdig uit in één proces. Richt elke handle op een roterende gateway en elke aanvraag verlaat automatisch een ander residential IP, zonder proxy-lijst administratie in uw code:

<?php
$urls = ['https://example.com/p/1', 'https://example.com/p/2', 'https://example.com/p/3'];

$mh = curl_multi_init();
$handles = [];

foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        // one gateway, a fresh residential exit per request
        CURLOPT_PROXY          => 'http://USER:PASS@rotating.quantumproxies.io:8000',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT        => 30,
        CURLOPT_ENCODING       => '',
    ]);
    curl_multi_add_handle($mh, $ch);
    $handles[$url] = $ch;
}

do {
    curl_multi_exec($mh, $running);
    curl_multi_select($mh);        // wait for activity instead of spinning
} while ($running > 0);

$results = [];
foreach ($handles as $url => $ch) {
    $results[$url] = curl_multi_getcontent($ch);
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}
curl_multi_close($mh);

Houd batches bescheiden — 8 tot 16 gelijktijdige handles is voldoende voor de meeste doelen, en pacing is belangrijker dan pure snelheid: een usleep() van enkele honderden milliseconden tussen batches kost u weinig en houdt de aanvraagfrequenties onder de drempels die 429's activeren. Controleer curl_getinfo($ch, CURLINFO_HTTP_CODE) per handle en routeer mislukkingen naar een herhaalwachtrij in plaats van inline opnieuw op te halen. Wanneer een stroom meerdere aanvragen omvat die één identiteit moeten delen (inloggen, winkelwagen, paginering met cookies), schakel die handle dan over naar een sticky sessie door de gebruikersnaam te suffixen: USER-session-a1b2c3 houdt hetzelfde exit-IP voor de duur in plaats van te roteren.

Diagram van een PHP scraping-pijplijn die curl_multi batches door een roterende residential proxy gateway stuurt
De worker blijft dom, de gateway blijft slim: rotatie, kleverigheid en geo-targeting zitten allemaal in de proxy-URL.

Headers en cookies: zie eruit als de browser die je beweert te zijn

Een schoon exit-IP met standaard cURL headers leest nog steeds als een bot — PHP kondigt zichzelf aan tenzij je de aanvraag aankleedt. Drie opties sluiten het grootste deel van de kloof: CURLOPT_USERAGENT met een actuele browserstring, CURLOPT_HTTPHEADER voor Accept-Language en dergelijke, en het cookiepaar CURLOPT_COOKIEJAR / CURLOPT_COOKIEFILE gericht op hetzelfde bestand, wat sessiecookies over handles heen behoudt zoals een browser doet. Cookie-persistentie is belangrijker dan mensen verwachten: veel sites stellen een token in bij de eerste hit en dagen stilletjes clients uit die het nooit teruggeven. Houd de geclaimde identiteit coherent — een Chrome User-Agent zonder Accept-Language en een kale header set is op zichzelf een vingerafdruk, en coherentiecontrole is precies hoe anti-bot leveranciers PHP scrapers bij hun tweede aanvraag vangen.

Guzzle: dezelfde proxy, betere ergonomie

<?php
use GuzzleHttp\Client;

$client = new Client([
    'proxy'           => 'http://USER:PASS@gate.quantumproxies.io:8000',
    'timeout'         => 30,
    'connect_timeout' => 10,
]);

$res = $client->get('https://httpbin.org/ip');
echo $res->getBody();

// per-request override, e.g. a sticky session for a login flow
$res = $client->get('https://example.com/account', [
    'proxy' => 'http://USER-session-a1b2c3:PASS@gate.quantumproxies.io:8000',
]);

Guzzle drijft cURL aan, dus alles hierboven geldt nog steeds — het leest gewoon beter. Eén waarschuwing: Guzzle's standaard timeout is 0, wat betekent wacht voor altijd. Een proxy-uitgang die halverwege de overdracht vastloopt, zal een PHP-FPM worker oneindig vastpinnen, dus stel beide time-outs in op elke client die je maakt. De proxy optie accepteert ook een array met http, https en no sleutels wanneer je per-schema routering of interne-host uitsluitingen nodig hebt.

Wanneer PHP cURL niet meer genoeg is

cURL haalt HTML op; het voert geen JavaScript uit. Als de respons terugkomt als een skeletpagina met lege divs, wordt de data client-gerenderd — onze gids over lege-pagina reacties laat zien hoe je het kunt detecteren. En op geharde doelen verraadt cURL's TLS-handshake je ongeacht hoe schoon het IP is (het browser-werkt-curl-403 mysterie). PHP kan redelijkerwijs geen headless browser per aanvraag draaien, dus de pragmatische uitweg is de Scraper API: één cURL-aanroep naar een enkele endpoint, en rendering, browser vingerafdrukken, rotatie en parsing komen terug als schone markdown, JSON of HTML.

Probeer de Scraper API vanuit PHP

Veelgestelde vragen

Hoe gebruik ik een proxy met PHP cURL?

Stel CURLOPT_PROXY in op de volledige proxy-URL, voeg CURLOPT_PROXYUSERPWD toe als het inloggegevens nodig heeft, en schakel altijd CURLOPT_RETURNTRANSFER en expliciete time-outs in. Dat is de volledige vereiste set — HTTPS-doelen tunnelen automatisch, en alles daarbuiten is optionele afstemming.

Hoe geef ik een proxy gebruikersnaam en wachtwoord door in PHP?

Ofwel curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:pass') of embed ze in de URL: http://user:pass@host:port. Als je HTTP 407 krijgt, heeft de proxy die inloggegevens afgewezen — controleer op typefouten en URL-encode speciale tekens. Providers die IP-whitelist authenticatie gebruiken, hebben helemaal geen inloggegevens nodig zodra het IP van je server is geautoriseerd.

Kan PHP cURL een SOCKS5 proxy gebruiken?

Ja. Geef een socks5h:// URL door aan CURLOPT_PROXY, of stel CURLOPT_PROXYTYPE in op CURLPROXY_SOCKS5_HOSTNAME. Geef de voorkeur aan de hostname-varianten boven gewone socks5 zodat DNS oplost bij de proxy-uitgang in plaats van je server — anders lekt elk doeldomein via je lokale resolver.

Wat is CURLOPT_HTTPPROXYTUNNEL en heb ik het nodig?

Het dwingt cURL om te tunnelen door de proxy met CONNECT in plaats van de proxy te vragen de URL op te halen. Voor https:// doelen tunnelt cURL automatisch, dus de optie is overbodig in bijna alle scraping code. Stel het alleen in wanneer je tunnelsemantiek nodig hebt voor een gewone HTTP-doel.

Hoe stel ik een time-out in voor PHP cURL via een proxy?

Stel beide in: CURLOPT_CONNECTTIMEOUT dekt het bereiken van de proxy en het openen van de tunnel, CURLOPT_TIMEOUT begrenst de hele overdracht. Milliseconde-varianten (_MS) bestaan voor strakkere budgetten. Zonder hen houdt een vastgelopen uitgang de aanvraag vast — en onder PHP-FPM, een hele worker — totdat de limiet van de webserver het beëindigt.

Kopieer de eerste snippet, houd de checklist in gedachten, en PHP is een perfect solide scraping-taal: curl_multi voor parallelisme, een roterende gateway voor identiteit, Guzzle voor ergonomie, en de Scraper API voor de pagina's die terugvechten.

Draai je PHP scraper op QuantumProxies residential IPs