Torna al blog

Configurazione del proxy in PHP tramite cURL e Guzzle HTTP Client: guida completa con esempi di codice

Guida completa alla configurazione di proxy in PHP tramite cURL e Guzzle HTTP Client - con esempi di codice, rotazione IP e consigli sulla scelta del tipo di proxy per il parsing e l'automazione.

📅12 agosto 2026
```html

Se stai scrivendo uno scraper, automatizzando la raccolta di dati o aggirando le limitazioni IP, non puoi fare a meno dei proxy in PHP. In questa guida esamineremo due strumenti principali: l'estensione integrata cURL e la popolare libreria Guzzle HTTP Client — con esempi di codice pronti all'uso nel tuo progetto.

Perché usare proxy nei progetti PHP

PHP rimane uno dei linguaggi più diffusi per l'automazione server-side, e le attività che richiedono proxy si presentano costantemente. Ecco i principali scenari in cui non puoi fare a meno dei proxy:

  • Scraping di siti web e marketplace — Wildberries, Ozon, Avito, AliExpress bloccano gli IP dopo alcune decine di richieste. I proxy consentono di distribuire il carico tra diversi indirizzi e di evitare errori 403/429.
  • Raccolta di dati da risorse geolocalizzate — i prezzi, i risultati dei motori di ricerca e i contenuti possono variare a seconda del paese. I proxy con la geolocalizzazione necessaria risolvono questo problema.
  • Bypassare il rate limiting — molte API limitano il numero di richieste da un singolo IP. La rotazione dei proxy consente di aggirare questi limiti.
  • Testare contenuti geolocalizzati — controllare come appare un sito per gli utenti di diversi paesi senza uscire dall'ufficio.
  • Anonimato durante l'automazione — nascondere il vero IP del server durante le richieste massicce a risorse esterne.
  • Monitoraggio della concorrenza — raccolta regolare di prezzi, assortimento e promozioni dai siti dei concorrenti senza rischiare di essere bloccati.

In PHP, per le richieste HTTP si utilizzano più frequentemente due strumenti: l'estensione integrata cURL e la libreria Guzzle HTTP Client. Entrambi supportano proxy HTTP, HTTPS, SOCKS4 e SOCKS5 — esaminiamo ciascuno in dettaglio.

Quale tipo di proxy scegliere per PHP

Prima di scrivere il codice, è importante capire quale tipo di proxy è adatto per il tuo compito. I diversi tipi hanno caratteristiche diverse in termini di velocità, affidabilità e grado di anonimato.

Tipo di proxy Velocità Anonimato Migliore per
Proxy di data center Molto alta Media Scraping senza sistemi anti-bot rigorosi, richieste API
Proxy residenziali Media Alta Scraping di siti protetti, marketplace, dati geolocalizzati
Proxy mobili Media Massima Siti con protezione anti-bot rigorosa, social media

Per la maggior parte delle attività di scraping di dati da marketplace come Wildberries o Ozon, la scelta ottimale saranno i proxy residenziali — i loro IP appartengono a veri utenti domestici, rendendo le richieste praticamente indistinguibili dal traffico normale del browser. I proxy di data center sono adatti dove la velocità è più importante della discrezione e la protezione è debole.

Tutti e tre i tipi di proxy supportano i protocolli HTTP/HTTPS e SOCKS5, quindi dal punto di vista del codice, la configurazione è la stessa. L'unica differenza è nella stringa di connessione.

Proxy in cURL: configurazione di base

L'estensione cURL è disponibile in PHP "out of the box" ed è il modo standard per effettuare richieste HTTP sul server. Per connettersi a un proxy si utilizzano due opzioni principali: CURLOPT_PROXY e CURLOPT_PROXYTYPE.

Proxy HTTP senza autenticazione

<?php

$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL            => 'https://httpbin.org/ip',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_TIMEOUT        => 30,

    // Indica l'indirizzo del proxy
    CURLOPT_PROXY          => '185.199.100.1:8080',

    // Tipo di proxy: HTTP (predefinito)
    CURLOPT_PROXYTYPE      => CURLPROXY_HTTP,
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "Codice HTTP: $httpCode\n";
echo $response;

Qui CURLOPT_PROXY accetta una stringa nel formato host:port. Per il traffico HTTPS tramite proxy HTTP, cURL utilizza automaticamente il metodo CONNECT — tunneling, quindi il contenuto della richiesta rimane crittografato.

Opzioni utili di cURL per lavorare con proxy

Opzione Descrizione
CURLOPT_PROXY Indirizzo del server proxy (host:port)
CURLOPT_PROXYTYPE Tipo: CURLPROXY_HTTP, CURLPROXY_SOCKS4, CURLPROXY_SOCKS5
CURLOPT_PROXYUSERPWD Nome utente e password nel formato user:password
CURLOPT_HTTPPROXYTUNNEL Abilita il tunneling tramite HTTP CONNECT
CURLOPT_SSL_VERIFYPEER Verifica del certificato SSL (false — disabilita)
CURLOPT_TIMEOUT Timeout della richiesta in secondi
CURLOPT_CONNECTTIMEOUT Timeout di connessione al proxy

Autenticazione e SOCKS5 in cURL

La maggior parte dei proxy commerciali richiede autenticazione tramite nome utente e password. Inoltre, SOCKS5 è il protocollo preferito quando è necessaria la massima anonimato, poiché non aggiunge intestazioni come X-Forwarded-For, che potrebbero rivelare l'uso del proxy.

Proxy HTTP con nome utente e password

<?php

$proxyHost = '185.199.100.1';
$proxyPort = '8080';
$proxyUser = 'your_login';
$proxyPass = 'your_password';

$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL            => 'https://httpbin.org/ip',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 30,
    CURLOPT_CONNECTTIMEOUT => 10,

    // Proxy con autenticazione
    CURLOPT_PROXY          => "$proxyHost:$proxyPort",
    CURLOPT_PROXYTYPE      => CURLPROXY_HTTP,
    CURLOPT_PROXYUSERPWD   => "$proxyUser:$proxyPass",

    // Tunneling per HTTPS
    CURLOPT_HTTPPROXYTUNNEL => true,

    // Intestazioni del browser per mascheramento
    CURLOPT_USERAGENT      => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    CURLOPT_HTTPHEADER     => [
        'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language: ru-RU,ru;q=0.9,en;q=0.8',
    ],
]);

$response = curl_exec($ch);

if (curl_errno($ch)) {
    echo 'Errore cURL: ' . curl_error($ch);
} else {
    echo $response;
}

curl_close($ch);

Proxy SOCKS5 in cURL

<?php

$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL            => 'https://httpbin.org/ip',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 30,

    CURLOPT_PROXY          => '185.199.100.1:1080',

    // SOCKS5 con risoluzione DNS tramite proxy (consigliato!)
    CURLOPT_PROXYTYPE      => CURLPROXY_SOCKS5_HOSTNAME,

    // Autenticazione (se necessaria)
    CURLOPT_PROXYUSERPWD   => 'login:password',
]);

$response = curl_exec($ch);
curl_close($ch);

echo $response;

💡 Importante: SOCKS5 vs SOCKS5_HOSTNAME

Usa CURLPROXY_SOCKS5_HOSTNAME invece di CURLPROXY_SOCKS5. La differenza è che con SOCKS5_HOSTNAME le richieste DNS passano anche attraverso il proxy, prevenendo la perdita di DNS e aumentando l'anonimato. Con SOCKS5 normale, DNS viene risolto localmente — questo può rivelare il tuo vero IP.

Rotazione dei proxy in cURL

Un singolo proxy verrà rapidamente bloccato durante richieste massicce. La strategia corretta è avere un pool di proxy e alternarli. Ecco una semplice ma efficace implementazione:

<?php

class ProxyRotator
{
    private array $proxies;
    private int $currentIndex = 0;

    public function __construct(array $proxies)
    {
        $this->proxies = $proxies;
        shuffle($this->proxies); // Mescola per ordine casuale
    }

    /**
     * Ottieni il prossimo proxy dal pool
     */
    public function getNext(): string
    {
        $proxy = $this->proxies[$this->currentIndex];
        $this->currentIndex = ($this->currentIndex + 1) % count($this->proxies);
        return $proxy;
    }

    /**
     * Ottieni un proxy casuale
     */
    public function getRandom(): string
    {
        return $this->proxies[array_rand($this->proxies)];
    }
}

// Lista di proxy nel formato login:password@host:port
$proxyList = [
    'user1:[email protected]:8080',
    'user2:[email protected]:8080',
    'user3:[email protected]:8080',
    'user4:[email protected]:8080',
];

$rotator = new ProxyRotator($proxyList);

/**
 * Funzione di richiesta con selezione automatica del proxy
 */
function fetchWithProxy(string $url, ProxyRotator $rotator): ?string
{
    $proxyStr = $rotator->getNext();

    // Analizza la stringa del proxy
    preg_match('/^(.+):(.+)@(.+):(\d+)$/', $proxyStr, $m);
    [, $user, $pass, $host, $port] = $m;

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 20,
        CURLOPT_CONNECTTIMEOUT => 8,
        CURLOPT_PROXY          => "$host:$port",
        CURLOPT_PROXYTYPE      => CURLPROXY_HTTP,
        CURLOPT_PROXYUSERPWD   => "$user:$pass",
        CURLOPT_USERAGENT      => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
        CURLOPT_SSL_VERIFYPEER => false,
    ]);

    $response = curl_exec($ch);
    $error    = curl_error($ch);
    curl_close($ch);

    if ($error) {
        error_log("Proxy $host:$port non disponibile: $error");
        return null;
    }

    return $response;
}

// Esempio: scraping di 10 pagine con rotazione dei proxy
$urls = [
    'https://example.com/page/1',
    'https://example.com/page/2',
    'https://example.com/page/3',
    // ...
];

foreach ($urls as $url) {
    $html = fetchWithProxy($url, $rotator);
    if ($html) {
        echo "Ricevuto: " . strlen($html) . " byte\n";
    }
    usleep(500000); // Pausa di 0.5 secondi tra le richieste
}

Fai attenzione a usleep(500000) — la pausa tra le richieste è criticamente importante. Anche con la rotazione dei proxy, richieste troppo frequenti possono portare a un blocco per modelli comportamentali. L'intervallo consigliato è di 500 ms a 2 secondi a seconda del sito.

Proxy in Guzzle HTTP Client: configurazione di base

Guzzle è una potente libreria PHP per le richieste HTTP, utilizzata nella maggior parte dei moderni framework PHP (Laravel, Symfony). Fornisce un'API più comoda e leggibile rispetto al cURL nudo, supporta richieste asincrone, middleware e una comoda gestione degli errori.

Installazione tramite Composer

composer require guzzlehttp/guzzle

Proxy HTTP senza autenticazione

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client([
    // URL di base (opzionale)
    'base_uri' => 'https://httpbin.org',

    // Impostazioni predefinite per tutte le richieste
    'timeout'  => 30,
    'connect_timeout' => 10,

    // Proxy per tutte le richieste del client
    'proxy' => 'http://185.199.100.1:8080',
]);

$response = $client->get('/ip');

echo $response->getStatusCode() . "\n";  // 200
echo $response->getBody() . "\n";        // {"origin": "185.199.100.1"}

In Guzzle, il proxy è impostato tramite l'opzione proxy nel formato URL. Questo è più intuitivo rispetto a cURL. Puoi impostare il proxy sia a livello di client (per tutte le richieste) che per ciascuna richiesta separatamente.

Proxy HTTP con autenticazione

<?php

use GuzzleHttp\Client;

$login    = 'your_login';
$password = 'your_password';
$host     = '185.199.100.1';
$port     = '8080';

$client = new Client([
    'timeout' => 30,
]);

// Proxy con autenticazione passato in formato URL
$response = $client->get('https://httpbin.org/ip', [
    'proxy' => "http://$login:$password@$host:$port",
    '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',
    ],
]);

echo $response->getBody();

Il formato dell'URL del proxy in Guzzle è: scheme://user:password@host:port. Per i proxy HTTP utilizziamo http://, per SOCKS5 — socks5://.

Lavorare con Guzzle: timeout, intestazioni, SOCKS5

Esaminiamo scenari più complessi: diversi proxy per HTTP e HTTPS, SOCKS5, disabilitazione della verifica SSL e impostazione delle intestazioni per simulare un browser.

Diversi proxy per HTTP e HTTPS

<?php

use GuzzleHttp\Client;

$client = new Client([
    'timeout' => 30,
    'proxy'   => [
        // Proxy per richieste HTTP
        'http'  => 'http://login:[email protected]:8080',
        // Proxy per richieste HTTPS
        'https' => 'http://login:[email protected]:8080',
        // Eccezioni — questi host vanno senza proxy
        'no'    => ['localhost', '127.0.0.1', '.internal.corp'],
    ],
]);

SOCKS5 in Guzzle

<?php

use GuzzleHttp\Client;

// Per SOCKS5 in Guzzle è necessario un pacchetto aggiuntivo
// composer require clue/socks-react (o usa curl handler)

$client = new Client([
    'timeout' => 30,
    'proxy'   => 'socks5://login:[email protected]:1080',
    'curl'    => [
        CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
    ],
]);

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

Configurazione completa con intestazioni del browser

<?php

use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;

$client = new Client([
    'timeout'         => 30,
    'connect_timeout' => 10,
    'verify'          => false, // Disabilita la verifica SSL (per debug)
    'allow_redirects' => [
        'max'       => 5,
        'strict'    => false,
        'referer'   => true,
        'protocols' => ['http', 'https'],
    ],
    'headers' => [
        'User-Agent'      => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept'          => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language' => 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
        'Accept-Encoding' => 'gzip, deflate, br',
        'Cache-Control'   => 'no-cache',
        'Connection'      => 'keep-alive',
    ],
]);

$response = $client->get('https://example.com/catalog', [
    'proxy' => 'http://login:[email protected]:8080',
]);

$statusCode = $response->getStatusCode();
$body       = (string) $response->getBody();

echo "Stato: $statusCode, Dimensione: " . strlen($body) . " byte\n";

⚠️ Riguardo a verify => false

Disabilitare la verifica SSL ('verify' => false) è accettabile solo per il debug o per lavorare con servizi interni. Nel codice di produzione, assicurati sempre di mantenere attiva la verifica SSL, altrimenti sei vulnerabile ad attacchi di tipo man-in-the-middle.

Rotazione dei proxy in Guzzle

In Guzzle, la rotazione è comodamente implementata tramite middleware — uno strato intermedio che intercetta ogni richiesta e aggiunge il proxy. Questo è più elegante rispetto a passare manualmente il proxy in ogni chiamata.

<?php

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;

/**
 * Middleware per la rotazione automatica dei proxy
 */
function proxyRotationMiddleware(array $proxies): callable
{
    $index = 0;
    $total = count($proxies);

    return Middleware::mapRequest(
        function (RequestInterface $request) use ($proxies, &$index, $total) {
            $proxy = $proxies[$index % $total];
            $index++;

            // Aggiungi il proxy agli attributi della richiesta
            // (passato tramite options nel handler)
            return $request->withHeader('X-Selected-Proxy', $proxy);
        }
    );
}

// Lista di proxy
$proxies = [
    'http://user1:[email protected]:8080',
    'http://user2:[email protected]:8080',
    'http://user3:[email protected]:8080',
];

// Variante semplice: classe wrapper su Guzzle con rotazione
class GuzzleWithRotation
{
    private Client $client;
    private array $proxies;
    private int $index = 0;

    public function __construct(array $proxies, array $clientConfig = [])
    {
        $this->proxies = $proxies;
        $this->client  = new Client($clientConfig);
    }

    public function get(string $url, array $options = []): \GuzzleHttp\Psr7\Response
    {
        $options['proxy'] = $this->proxies[$this->index % count($this->proxies)];
        $this->index++;

        return $this->client->get($url, $options);
    }

    public function post(string $url, array $options = []): \GuzzleHttp\Psr7\Response
    {
        $options['proxy'] = $this->proxies[$this->index % count($this->proxies)];
        $this->index++;

        return $this->client->post($url, $options);
    }
}

// Utilizzo
$guzzle = new GuzzleWithRotation($proxies, [
    'timeout' => 30,
    'headers' => [
        'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
    ],
]);

$urls = [
    'https://example.com/product/1',
    'https://example.com/product/2',
    'https://example.com/product/3',
];

foreach ($urls as $url) {
    try {
        $response = $guzzle->get($url);
        echo "OK [{$response->getStatusCode()}]: $url\n";
        sleep(1);
    } catch (\Exception $e) {
        echo "Errore: " . $e->getMessage() . "\n";
    }
}

Gestione degli errori e debug

Quando si lavora con i proxy, gli errori sono inevitabili: il proxy potrebbe non essere disponibile, rispondere lentamente o il sito di destinazione potrebbe restituire un errore. È importante gestire correttamente tutte queste situazioni.

Gestione degli errori in cURL

<?php

function fetchWithErrorHandling(string $url, string $proxy): array
{
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 20,
        CURLOPT_CONNECTTIMEOUT => 8,
        CURLOPT_PROXY          => $proxy,
        CURLOPT_PROXYTYPE      => CURLPROXY_HTTP,
    ]);

    $body     = curl_exec($ch);
    $errno    = curl_errno($ch);
    $error    = curl_error($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $totalTime = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
    curl_close($ch);

    // Codici di errore cURL per i proxy
    $proxyErrors = [
        CURLE_COULDNT_CONNECT    => 'Impossibile connettersi al proxy',
        CURLE_OPERATION_TIMEDOUT => 'Timeout di connessione al proxy',
        CURLE_RECV_ERROR         => 'Errore nella ricezione dei dati',
        CURLE_SSL_CONNECT_ERROR  => 'Errore SSL tramite proxy',
    ];

    if ($errno) {
        $message = $proxyErrors[$errno] ?? "Errore cURL #$errno: $error";
        return ['success' => false, 'error' => $message, 'code' => $errno];
    }

    if ($httpCode >= 400) {
        return ['success' => false, 'error' => "HTTP $httpCode", 'code' => $httpCode];
    }

    return [
        'success' => true,
        'body'    => $body,
        'code'    => $httpCode,
        'time'    => round($totalTime, 3),
    ];
}

// Utilizzo con tentativi ripetuti
function fetchWithRetry(string $url, array $proxies, int $maxRetries = 3): ?string
{
    foreach ($proxies as $proxy) {
        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            $result = fetchWithErrorHandling($url, $proxy);

            if ($result['success']) {
                echo "Successo tramite $proxy (tentativo $attempt, {$result['time']}s)\n";
                return $result['body'];
            }

            echo "Errore tramite $proxy: {$result['error']}\n";

            if ($attempt < $maxRetries) {
                sleep(2); // Pausa prima di ripetere
            }
        }
    }

    return null; // Tutti i proxy non hanno funzionato
}

Gestione degli errori in Guzzle

<?php

use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Exception\ClientException;

$client = new Client(['timeout' => 30]);

$proxies = [
    'http://user1:[email protected]:8080',
    'http://user2:[email protected]:8080',
];

function fetchGuzzleWithFallback(Client $client, string $url, array $proxies): ?string
{
    foreach ($proxies as $proxy) {
        try {
            $response = $client->get($url, [
                'proxy'   => $proxy,
                'timeout' => 20,
            ]);

            return (string) $response->getBody();

        } catch (ConnectException $e) {
            // Proxy non disponibile o timeout di connessione
            echo "Proxy non disponibile ($proxy): " . $e->getMessage() . "\n";

        } catch (ClientException $e) {
            // HTTP 4xx — errore del client (403, 404, 429)
            $code = $e->getResponse()->getStatusCode();
            echo "HTTP $code per $url tramite $proxy\n";

            if ($code === 429) {
                echo "Rate limit — pausa di 5 secondi\n";
                sleep(5);
            }

        } catch (ServerException $e) {
            // HTTP 5xx — errore del server
            echo "Errore del server: " . $e->getResponse()->getStatusCode() . "\n";

        } catch (RequestException $e) {
            // Altri errori di richiesta
            echo "Errore di richiesta: " . $e->getMessage() . "\n";
        }
    }

    return null;
}

cURL vs Guzzle: cosa scegliere

Entrambi gli strumenti funzionano bene con i proxy, ma hanno punti di forza diversi. Ecco un confronto basato su criteri chiave:

Criterio cURL Guzzle
Installazione ✅ Integrato in PHP ⚠️ Richiede Composer
Leggibilità del codice ⚠️ Molte opzioni, verbose ✅ API pulita e comoda
Prestazioni ✅ Un po' più veloce (senza wrapper) ✅ Comparabile, supporta async
Richieste asincrone ⚠️ curl_multi (complesso) ✅ Supporto integrato
Middleware / hook ❌ No ✅ HandlerStack, Middleware
Supporto SOCKS5 ✅ Nativo ✅ Tramite curl handler
Gestione degli errori ⚠️ Codici di errore manuali ✅ Eccezioni con tipi
Integrazione in Laravel/Symfony ⚠️ Manuale ✅ Supporto nativo
Adatto per Script semplici, senza dipendenze Progetti su framework, logica complessa

Raccomandazione: Se stai scrivendo uno script semplice o lavori su un hosting senza Composer, usa cURL. Per progetti su Laravel, Symfony o qualsiasi moderna applicazione PHP, Guzzle sarà significativamente più comodo e offrirà maggiori possibilità di scalabilità.

🚀 Checklist rapida prima del lancio

  • ✅ Controlla che il proxy funzioni: curl -x http://login:pass@host:port https://httpbin.org/ip
  • ✅ Imposta i timeout: timeout e connect_timeout
  • ✅ Aggiungi un User-Agent realistico
  • ✅ Implementa pause tra le richieste (minimo 500 ms)
  • ✅ Prevedi la gestione degli errori e il fallback su un altro proxy
  • ✅ Per SOCKS5 usa CURLPROXY_SOCKS5_HOSTNAME (non SOCKS5)
  • ✅ Non disabilitare la verifica SSL in produzione

Conclusione

Configurare i proxy in PHP è un compito non difficile se si conoscono le opzioni e i formati corretti. Per compiti di base, è sufficiente cURL con CURLOPT_PROXY e CURLOPT_PROXYUSERPWD. Per progetti su framework, Guzzle offre un'API più comoda, eccezioni tipizzate e supporto middleware per la rotazione dei proxy.

I principi chiave per un funzionamento affidabile: utilizza SOCKS5 con risoluzione DNS tramite proxy (CURLPROXY_SOCKS5_HOSTNAME), imposta sempre i timeout, aggiungi pause tra le richieste e implementa la rotazione dei proxy per attività su larga scala. La gestione degli errori con fallback su un altro proxy è un elemento obbligatorio di qualsiasi codice di produzione.

```