# Scraping com Proxy PHP cURL: CURLOPT_PROXY, Autenticação e Guzzle

A maioria dos snippets de proxy PHP online ainda possuem os mesmos três bugs de 2011. Aqui está uma configuração limpa de CURLOPT_PROXY — autenticação, SOCKS5, rotação com curl_multi, Guzzle e timeouts — que se mantém em produção.

O código de proxy PHP cURL tem um problema peculiar: os snippets mais bem classificados têm uma década e muitos contêm bugs reais — chamadas duplas de `curl_exec()` que disparam cada solicitação duas vezes, `CURLOPT_SSL_VERIFYPEER` desativado "para fazer o HTTPS funcionar", opções definidas para valores que já eram padrão. Este guia é a versão atual e correta: `CURLOPT_PROXY` com autenticação, SOCKS5, rotação paralela com `curl_multi`, a mesma configuração no Guzzle e timeouts que impedem um trabalhador de ficar travado a noite toda.

## Um exemplo mínimo e correto de 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
```

As credenciais podem estar em `CURLOPT_PROXYUSERPWD` como mostrado, ou embutidas na URL do proxy (`http://USER:PASS@host:port`) — ambas funcionam. `CURLOPT_RETURNTRANSFER` é o que os iniciantes esquecem: sem ele, `curl_exec()` ecoa a resposta diretamente na saída e retorna `true`, o que nunca é o que um scraper deseja. No PHP 8, o handle é um objeto `CurlHandle` em vez de um recurso; as chamadas de opção permanecem inalteradas.

## As opções que as pessoas copiam sem pensar (e um erro real de segurança)

- `CURLOPT_HTTPPROXYTUNNEL` — não é necessário para alvos HTTPS. cURL estabelece o túnel CONNECT automaticamente sempre que o alvo é `https://`; esta opção só força a tunelização para alvos HTTP simples, o que a raspagem quase nunca requer.
- `CURLOPT_CUSTOMREQUEST => 'GET'` — GET já é o padrão; defini-lo não faz nada além de quebrar o tratamento de redirecionamento em casos extremos.
- Chamar `curl_exec()` duas vezes — o clássico bug do Stack Overflow: a solicitação dispara duas vezes e o segundo valor de retorno sobrescreve o primeiro. Uma vez por handle.
- `CURLOPT_SSL_VERIFYPEER => false` — este é realmente perigoso. Ele "corrige" erros de certificado permitindo ataques man-in-the-middle em cada solicitação. A correção real, de acordo com o manual do PHP, é apontar `curl.cainfo` no php.ini para um pacote `cacert.pem` atualizado.

![Um curl_setopt_array em PHP com as seis opções a definir: CURLOPT_PROXY, PROXYUSERPWD, RETURNTRANSFER, CONNECTTIMEOUT 10, TIMEOUT 30 e ENCODING, e depois a dívida de copiar e colar a apagar: HTTPPROXYTUNNEL, CUSTOMREQUEST GET, SSL_VERIFYPEER false e um curl_exec duplicado. O quadro diz para corrigir os certificados com curl.cainfo em vez disso.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335147/blog/generated/qp-b9-008-diag1.png)
*Seis opções realizam todo o trabalho; a coluna da direita é dívida de cópia e colagem de tutoriais da era 2011.*

## Proxies SOCKS5 em 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');
```

O esquema decide onde o DNS acontece: `socks5://` (e `CURLPROXY_SOCKS5`) resolve nomes de host no seu servidor, vazando cada domínio alvo para o seu resolvedor local; `socks5h://` (`CURLPROXY_SOCKS5_HOSTNAME`) resolve na saída do proxy. Prefira o último. QuantumProxies oferece [HTTP e SOCKS5 em todos os planos](https://quantumproxies.io/socks5-proxies), então mudar de protocolo é uma alteração de uma linha, não uma nova assinatura. A mesma regra de DNS se aplica na linha de comando — veja nossas [receitas de proxy curl](https://quantumproxies.io/blog/curl-with-proxy-examples) para os equivalentes de shell de tudo nesta página.

## Rotação e solicitações paralelas com curl_multi

PHP não tem async/await, mas não precisa de um para scraping paralelo — `curl_multi` executa dezenas de transferências simultaneamente em um único processo. Aponte cada handle para um gateway rotativo e cada solicitação sai automaticamente de um [IP residencial](https://quantumproxies.io/residential-proxies) diferente, sem necessidade de manutenção de lista de proxies no seu código:

```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);
```

Mantenha os lotes modestos — 8 a 16 handles simultâneos são suficientes para a maioria dos alvos, e o ritmo importa mais do que a velocidade bruta: um `usleep()` de algumas centenas de milissegundos entre os lotes custa pouco e mantém as taxas de solicitação abaixo dos limites que acionam 429s. Verifique `curl_getinfo($ch, CURLINFO_HTTP_CODE)` por handle e encaminhe falhas para uma fila de tentativas em vez de refazer inline. Quando um fluxo abrange várias solicitações que devem compartilhar uma identidade (login, carrinho, paginação com cookies), altere esse handle para uma sessão fixa adicionando um sufixo ao nome de usuário: `USER-sid-a1b2c3-ttl-30m` mantém o mesmo IP de saída durante a duração em vez de rotacionar.

![Um worker PHP envia lotes curl_multi de 8 a 16 handles por uma única URL de proxy, e o gateway decide a saída: um IP residencial novo e rotativo, uma sessão fixa que mantém a mesma saída ou uma saída geolocalizada. O quadro: a rotação, o nome de usuário fixo USER-session-a1b2c3 e o geotargeting ficam todos na URL do proxy.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335147/blog/generated/qp-b9-008-diag2.png)
*O trabalhador permanece simples, o gateway permanece inteligente: rotação, fixação e direcionamento geográfico vivem todos na URL do proxy.*

## Cabeçalhos e cookies: pareça o navegador que você afirma ser

Um IP de saída limpo com cabeçalhos cURL padrão ainda é lido como um bot — PHP se anuncia a menos que você vista a solicitação. Três opções fecham a maioria das lacunas: `CURLOPT_USERAGENT` com uma string de navegador atual, `CURLOPT_HTTPHEADER` para `Accept-Language` e similares, e o par de cookies `CURLOPT_COOKIEJAR` / `CURLOPT_COOKIEFILE` apontados para o mesmo arquivo, que persiste cookies de sessão entre handles da mesma forma que um navegador faz. A persistência de cookies importa mais do que as pessoas esperam: muitos sites definem um token no primeiro acesso e desafiam silenciosamente clientes que nunca o retornam. Mantenha a identidade reivindicada coerente — um User-Agent do Chrome sem `Accept-Language` e um conjunto de cabeçalhos simples é em si uma impressão digital, e a verificação de coerência é exatamente como fornecedores anti-bot de nível médio pegam scrapers PHP em sua segunda solicitação.

## Guzzle: o mesmo proxy, ergonomia melhor

```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 dirige o cURL por baixo, então tudo acima ainda se aplica — só fica mais legível. Um aviso: o `timeout` padrão do Guzzle é 0, significando esperar para sempre. Uma saída de proxy que trava no meio da transferência fixará um trabalhador PHP-FPM indefinidamente, então defina ambos os timeouts em cada cliente que você construir. A opção `proxy` também aceita um array com as chaves `http`, `https` e `no` quando você precisa de roteamento por esquema ou exclusões de host interno.

## Quando o PHP cURL deixa de ser suficiente

cURL busca HTML; ele não executa JavaScript. Se a resposta vier como uma página esqueleto com divs vazios, os dados são renderizados no cliente — nosso guia sobre [respostas de página vazia](https://quantumproxies.io/blog/scraper-returns-empty-page-javascript) mostra como detectar isso. E em alvos protegidos, o próprio handshake TLS do cURL te denuncia, não importa quão limpo seja o IP (o mistério do [browser-funciona-curl-403](https://quantumproxies.io/blog/curl-403-but-browser-works)). PHP não pode razoavelmente executar um navegador headless por solicitação, então a saída pragmática é a [Scraper API](https://quantumproxies.io/scraper-api): uma chamada cURL para um único endpoint, e a renderização, impressões digitais do navegador, rotação e análise retornam como markdown, JSON ou HTML limpos.

[Experimente a Scraper API a partir do PHP](https://quantumproxies.io/scraper-api)

## Perguntas frequentes

### Como uso um proxy com PHP cURL?

Defina `CURLOPT_PROXY` para a URL completa do proxy, adicione `CURLOPT_PROXYUSERPWD` se precisar de credenciais, e sempre ative `CURLOPT_RETURNTRANSFER` além de timeouts explícitos. Esse é o conjunto necessário — alvos HTTPS tunelam automaticamente, e todo o resto é ajuste opcional.

### Como passo um nome de usuário e senha de proxy no PHP?

Ou `curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:pass')` ou embuta-os na URL: `http://user:pass@host:port`. Se você receber HTTP 407, o proxy rejeitou essas credenciais — verifique se há erros de digitação e codifique caracteres especiais na URL. Provedores que usam autenticação por lista branca de IP não precisam de credenciais assim que o IP do seu servidor está autorizado.

### O PHP cURL pode usar um proxy SOCKS5?

Sim. Passe uma URL `socks5h://` para `CURLOPT_PROXY`, ou defina `CURLOPT_PROXYTYPE` para `CURLPROXY_SOCKS5_HOSTNAME`. Prefira as variantes de hostname sobre o `socks5` simples para que o DNS seja resolvido na saída do proxy em vez do seu servidor — caso contrário, cada domínio alvo vaza através do seu resolvedor local.

### O que é CURLOPT_HTTPPROXYTUNNEL e eu preciso dele?

Ele força o cURL a tunelar através do proxy com CONNECT em vez de pedir ao proxy para buscar a URL. Para alvos `https://` o cURL tunela automaticamente, então a opção é redundante em quase todo código de scraping. Defina-a apenas quando precisar de semântica de túnel para um alvo HTTP simples.

### Como defino um timeout para PHP cURL através de um proxy?

Defina ambos: `CURLOPT_CONNECTTIMEOUT` cobre alcançar o proxy e abrir o túnel, `CURLOPT_TIMEOUT` limita toda a transferência. Variantes em milissegundos (`_MS`) existem para orçamentos mais apertados. Sem eles, uma saída travada mantém a solicitação — e sob PHP-FPM, um trabalhador inteiro — até que o próprio limite do servidor web a mate.

Copie o primeiro snippet, mantenha a lista de verificação em mente, e o PHP é uma linguagem de scraping perfeitamente sólida: `curl_multi` para paralelismo, um gateway rotativo para identidade, Guzzle para ergonomia, e a Scraper API para as páginas que resistem.

[Execute seu scraper PHP em IPs residenciais da QuantumProxies](https://quantumproxies.io/residential-proxies)
