# PHP cURL Proxy Scraping: CURLOPT_PROXY, Auth i Guzzle

Większość fragmentów kodu proxy w PHP dostępnych online wciąż zawiera te same trzy błędy z 2011 roku. Oto czysta konfiguracja CURLOPT_PROXY — autoryzacja, SOCKS5, rotacja curl_multi, Guzzle i limity czasu — która sprawdza się w produkcji.

Kod proxy PHP cURL ma specyficzny problem: najwyżej oceniane fragmenty mają dekadę, a wiele z nich zawiera prawdziwe błędy — podwójne wywołania `curl_exec()`, które uruchamiają każde żądanie dwa razy, `CURLOPT_SSL_VERIFYPEER` wyłączone "aby HTTPS działało", opcje ustawione na wartości, które już były domyślne. Ten przewodnik to aktualna, poprawna wersja: `CURLOPT_PROXY` z autoryzacją, SOCKS5, równoległa rotacja z `curl_multi`, ta sama konfiguracja w Guzzle i limity czasu, które zapobiegają zawieszaniu się pracownika na całą noc.

## Minimalny, poprawny przykład proxy PHP cURL

```php
<?php
function fetch(string $url): string|false {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_PROXY          => 'http://residentialpremium.quantumproxies.io:8080',
        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
```

Dane uwierzytelniające mogą być umieszczone w `CURLOPT_PROXYUSERPWD`, jak pokazano, lub w linii w URL proxy (`http://USER:PASS@host:port`) — obie metody działają. `CURLOPT_RETURNTRANSFER` to opcja, której początkujący często nie zauważają: bez niej `curl_exec()` wyświetla odpowiedź bezpośrednio na wyjściu i zwraca `true`, co nigdy nie jest tym, czego chce scraper. W PHP 8 uchwyt to obiekt `CurlHandle`, a nie zasób; wywołania opcji pozostają bez zmian.

## Opcje, które ludzie kopiują bez zrozumienia (i jeden prawdziwy błąd bezpieczeństwa)

- `CURLOPT_HTTPPROXYTUNNEL` — niepotrzebne dla celów HTTPS. cURL automatycznie ustanawia tunel CONNECT, gdy cel to `https://`; ta opcja wymusza tunelowanie dla celów HTTP, co prawie nigdy nie jest wymagane podczas scrapingu.
- `CURLOPT_CUSTOMREQUEST => 'GET'` — GET jest już domyślną metodą; ustawienie jej nic nie zmienia, poza psuciem obsługi przekierowań w skrajnych przypadkach.
- Podwójne wywołanie `curl_exec()` — klasyczny błąd z Stack Overflow: żądanie uruchamia się dwa razy, a druga wartość zwrotna nadpisuje pierwszą. Raz na uchwyt.
- `CURLOPT_SSL_VERIFYPEER => false` — naprawdę niebezpieczny. "Naprawia" błędy certyfikatów, pozwalając na ataki typu man-in-the-middle przy każdym żądaniu. Prawdziwa naprawa, zgodnie z podręcznikiem PHP, polega na wskazaniu `curl.cainfo` w php.ini na aktualny pakiet `cacert.pem`.

![curl_setopt_array w PHP z sześcioma opcjami do ustawienia: CURLOPT_PROXY, PROXYUSERPWD, RETURNTRANSFER, CONNECTTIMEOUT 10, TIMEOUT 30 i ENCODING, a potem skopiowany balast do usunięcia: HTTPPROXYTUNNEL, CUSTOMREQUEST GET, SSL_VERIFYPEER false i podwójne curl_exec. Karta mówi, by zamiast tego naprawić certyfikaty przez curl.cainfo.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335147/blog/generated/qp-b9-008-diag1.png)
*Sześć opcji obsługuje całą pracę; prawa kolumna to dług kopiowania-wklejania z poradników z 2011 roku.*

## SOCKS5 proxy w PHP

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

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

Schemat decyduje, gdzie odbywa się rozwiązywanie DNS: `socks5://` (i `CURLPROXY_SOCKS5`) rozwiązuje nazwy hostów na twoim serwerze, ujawniając każdą docelową domenę twojemu lokalnemu resolverowi; `socks5h://` (`CURLPROXY_SOCKS5_HOSTNAME`) rozwiązuje na wyjściu proxy. Preferuj to drugie. QuantumProxies oferuje [HTTP i SOCKS5 w każdym planie](https://quantumproxies.io/socks5-proxies), więc zmiana protokołów to zmiana jednej linii, a nie nowa subskrypcja. Ta sama zasada DNS dotyczy wiersza poleceń — zobacz nasze [przepisy curl proxy](https://quantumproxies.io/blog/curl-with-proxy-examples) dla odpowiedników powłoki wszystkiego na tej stronie.

## Rotacja i równoległe żądania z curl_multi

PHP nie ma async/await, ale nie potrzebuje ich do równoległego scrapingu — `curl_multi` uruchamia dziesiątki transferów jednocześnie w jednym procesie. Skieruj każdy uchwyt na rotującą bramę, a każde żądanie automatycznie wychodzi z innego [adresu IP rezydencyjnego](https://quantumproxies.io/residential-proxies), bez konieczności prowadzenia księgowości listy proxy w twoim kodzie:

```php
<?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@residentialpremium.quantumproxies.io:8080',
        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);
```

Utrzymuj partie umiarkowane — 8 do 16 równoczesnych uchwytów wystarczy dla większości celów, a tempo jest ważniejsze niż czysta prędkość: `usleep()` o kilka setek milisekund między partiami kosztuje cię niewiele i utrzymuje tempo żądań poniżej progów, które wyzwalają 429. Sprawdź `curl_getinfo($ch, CURLINFO_HTTP_CODE)` dla każdego uchwytu i kieruj niepowodzenia do kolejki ponownych prób zamiast ponownego pobierania w linii. Gdy przepływ obejmuje kilka żądań, które muszą dzielić jedną tożsamość (logowanie, koszyk, paginacja z ciasteczkami), przełącz ten uchwyt na sesję klejącą, dodając do nazwy użytkownika: `USER-sid-a1b2c3-ttl-30m` utrzymuje ten sam adres IP wyjścia przez cały czas trwania zamiast rotacji.

![Worker PHP wysyła partie curl_multi po 8 do 16 uchwytów przez jeden URL proxy, a brama decyduje o wyjściu: nowe rotujące IP rezydencyjne, sesja sticky utrzymująca to samo wyjście lub wyjście z geotargetowaniem. Karta: rotacja, nazwa użytkownika sticky USER-session-a1b2c3 i geotargetowanie są zapisane w URL proxy.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335147/blog/generated/qp-b9-008-diag2.png)
*Pracownik pozostaje prosty, brama pozostaje inteligentna: rotacja, lepkość i geotargetowanie są zawarte w URL proxy.*

## Nagłówki i ciasteczka: wyglądaj jak przeglądarka, którą twierdzisz, że jesteś

Czysty adres IP wyjściowy z domyślnymi nagłówkami cURL wciąż wygląda jak bot — PHP ogłasza się, chyba że ubierzesz żądanie. Trzy opcje zamykają większość luki: `CURLOPT_USERAGENT` z aktualnym ciągiem przeglądarki, `CURLOPT_HTTPHEADER` dla `Accept-Language` i podobnych, oraz para ciasteczek `CURLOPT_COOKIEJAR` / `CURLOPT_COOKIEFILE` wskazująca na ten sam plik, która utrzymuje sesyjne ciasteczka między uchwytami tak, jak robi to przeglądarka. Trwałość ciasteczek ma większe znaczenie, niż się spodziewają ludzie: wiele stron ustawia token przy pierwszym trafieniu i cicho wyzywa klientów, którzy nigdy go nie zwracają. Utrzymuj spójną tożsamość — User-Agent Chrome bez `Accept-Language` i nagie ustawienie nagłówków to samo w sobie odcisk palca, a sprawdzanie spójności to dokładnie sposób, w jaki średniej klasy dostawcy anty-botów łapią scrapery PHP przy drugim żądaniu.

## Guzzle: to samo proxy, lepsza ergonomia

```php
<?php
use GuzzleHttp\Client;

$client = new Client([
    'proxy'           => 'http://USER:PASS@residentialpremium.quantumproxies.io:8080',
    '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-sid-a1b2c3-ttl-30m:PASS@residentialpremium.quantumproxies.io:8080',
]);
```

Guzzle napędza cURL pod spodem, więc wszystko powyżej nadal ma zastosowanie — po prostu lepiej się czyta. Jedno ostrzeżenie: domyślny `timeout` w Guzzle to 0, co oznacza czekanie na zawsze. Wyjście proxy, które zatrzymuje się w trakcie transferu, przypnie pracownika PHP-FPM na czas nieokreślony, więc ustaw oba limity czasu na każdym kliencie, którego tworzysz. Opcja `proxy` akceptuje również tablicę z kluczami `http`, `https` i `no`, gdy potrzebujesz routingu per schemat lub wykluczeń hostów wewnętrznych.

## Kiedy PHP cURL przestaje wystarczać

cURL pobiera HTML; nie wykonuje JavaScriptu. Jeśli odpowiedź wraca jako szkielet strony z pustymi divami, dane są renderowane po stronie klienta — nasz przewodnik na temat [odpowiedzi pustej strony](https://quantumproxies.io/blog/scraper-returns-empty-page-javascript) pokazuje, jak to wykryć. A na twardych celach, sam handshake TLS cURL zdradza cię, niezależnie od tego, jak czysty jest IP (zagadkę [przeglądarka działa, cURL 403](https://quantumproxies.io/blog/curl-403-but-browser-works)). PHP nie może rozsądnie uruchamiać przeglądarki bezgłowej na każde żądanie, więc pragmatycznym wyjściem awaryjnym jest [Scraper API](https://quantumproxies.io/scraper-api): jedno wywołanie cURL do jednego punktu końcowego, a renderowanie, odciski palców przeglądarki, rotacja i parsowanie wracają jako czysty markdown, JSON lub HTML.

[Wypróbuj Scraper API z PHP](https://quantumproxies.io/scraper-api)

## Często zadawane pytania

### Jak używać proxy z PHP cURL?

Ustaw `CURLOPT_PROXY` na pełny URL proxy, dodaj `CURLOPT_PROXYUSERPWD`, jeśli wymaga danych uwierzytelniających, i zawsze włącz `CURLOPT_RETURNTRANSFER` plus wyraźne limity czasu. To cały wymagany zestaw — cele HTTPS tunelują automatycznie, a wszystko inne to opcjonalne dostrajanie.

### Jak przekazać nazwę użytkownika i hasło proxy w PHP?

Albo `curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:pass')`, albo osadź je w URL: `http://user:pass@host:port`. Jeśli otrzymasz HTTP 407, proxy odrzuciło te dane uwierzytelniające — sprawdź literówki i zakoduj specjalne znaki w URL. Dostawcy, którzy używają autoryzacji białej listy IP, nie potrzebują żadnych danych uwierzytelniających, gdy tylko IP twojego serwera jest autoryzowane.

### Czy PHP cURL może używać proxy SOCKS5?

Tak. Przekaż URL `socks5h://` do `CURLOPT_PROXY`, lub ustaw `CURLOPT_PROXYTYPE` na `CURLPROXY_SOCKS5_HOSTNAME`. Preferuj warianty z nazwą hosta nad zwykłym `socks5`, aby DNS rozwiązywał się na wyjściu proxy, a nie na twoim serwerze — w przeciwnym razie każda docelowa domena wycieka przez twój lokalny resolver.

### Co to jest CURLOPT_HTTPPROXYTUNNEL i czy go potrzebuję?

Wymusza na cURL tunelowanie przez proxy za pomocą CONNECT zamiast proszenia proxy o pobranie URL. Dla celów `https://` cURL tuneluje automatycznie, więc opcja jest zbędna w prawie całym kodzie scrapingu. Ustaw ją tylko wtedy, gdy potrzebujesz semantyki tunelu dla celu HTTP.

### Jak ustawić limit czasu dla PHP cURL przez proxy?

Ustaw oba: `CURLOPT_CONNECTTIMEOUT` obejmuje dotarcie do proxy i otwarcie tunelu, `CURLOPT_TIMEOUT` ogranicza cały transfer. Istnieją warianty milisekundowe (`_MS`) dla bardziej rygorystycznych budżetów. Bez nich zatrzymane wyjście utrzymuje żądanie — a pod PHP-FPM, całego pracownika — do momentu, gdy limit samego serwera WWW go nie zabije.

Skopiuj pierwszy fragment, miej na uwadze listę kontrolną, a PHP jest doskonale solidnym językiem do scrapingu: `curl_multi` dla równoległości, rotująca brama dla tożsamości, Guzzle dla ergonomii i Scraper API dla stron, które się bronią.

[Uruchom swój scraper PHP na adresach IP rezydencyjnych QuantumProxies](https://quantumproxies.io/residential-proxies)
