Se você está escrevendo um parser, automatizando a coleta de dados ou contornando restrições de IP — não há como evitar o uso de proxy em PHP. Neste guia, vamos explorar duas ferramentas principais: a extensão embutida cURL e a popular biblioteca Guzzle HTTP Client — com exemplos de código prontos para serem usados em seu projeto.
Por que usar proxy em projetos PHP
PHP continua sendo uma das linguagens mais populares para automação no servidor, e tarefas que exigem proxy são comuns. Aqui estão os principais cenários onde o uso de proxy é essencial:
- Scraping de sites e marketplaces — Wildberries, Ozon, Avito, AliExpress bloqueiam IPs após algumas dezenas de requisições. Proxies permitem distribuir a carga entre diferentes endereços e evitar 403/429.
- Coleta de dados de recursos geodependentes — preços, resultados de buscadores, conteúdo podem variar dependendo do país. Proxies com a geolocalização necessária resolvem esse problema.
- Contornar rate limiting — muitas APIs limitam o número de requisições de um único IP. A rotação de proxies permite contornar esses limites.
- Teste de conteúdo geodependente — verificar como o site aparece para usuários de diferentes países, sem sair do escritório.
- Anonimato na automação — ocultar o IP real do servidor ao fazer requisições em massa a recursos externos.
- Monitoramento de concorrentes — coleta regular de preços, sortimentos, promoções de sites concorrentes sem risco de ser banido.
Em PHP, para requisições HTTP, normalmente usamos duas ferramentas: a extensão embutida cURL e a biblioteca Guzzle HTTP Client. Ambas suportam proxies HTTP, HTTPS, SOCKS4 e SOCKS5 — vamos analisar cada uma em detalhes.
Qual tipo de proxy escolher para PHP
Antes de escrever o código, é importante entender qual tipo de proxy é adequado para sua tarefa. Diferentes tipos têm características distintas em termos de velocidade, confiabilidade e grau de anonimato.
| Tipo de Proxy | Velocidade | Anonimato | Melhor para |
|---|---|---|---|
| Proxies de Data Center | Muito alta | Média | Scraping sem sistemas de anti-bot rigorosos, requisições de API |
| Proxies Residenciais | Média | Alta | Scraping de sites protegidos, marketplaces, dados geográficos |
| Proxies Móveis | Média | Máxima | Sites com proteção anti-bot rigorosa, redes sociais |
Para a maioria das tarefas de scraping de dados em marketplaces como Wildberries ou Ozon, a escolha ideal são os proxies residenciais — seus IPs pertencem a usuários reais, tornando as requisições praticamente indistinguíveis do tráfego normal de navegador. Proxies de data center são adequados onde a velocidade é mais importante que a invisibilidade, e a proteção é fraca.
Todos os três tipos de proxy suportam os protocolos HTTP/HTTPS e SOCKS5, portanto, em termos de código — a configuração é a mesma. A única diferença está na string de conexão.
Proxy em cURL: configuração básica
A extensão cURL está disponível no PHP por padrão e é a maneira padrão de executar requisições HTTP no servidor. Para conectar um proxy, usamos duas opções principais: CURLOPT_PROXY e CURLOPT_PROXYTYPE.
Proxy HTTP sem autenticação
<?php
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/ip',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
// Especificamos o endereço do proxy
CURLOPT_PROXY => '185.199.100.1:8080',
// Tipo de proxy: HTTP (por padrão)
CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "Código HTTP: $httpCode\n";
echo $response;
Aqui CURLOPT_PROXY aceita uma string no formato host:port. Para tráfego HTTPS através de um proxy HTTP, o cURL usa automaticamente o método CONNECT — tunelamento, portanto, o conteúdo da requisição permanece criptografado.
Opções úteis do cURL para trabalhar com proxies
| Opção | Descrição |
|---|---|
CURLOPT_PROXY |
Endereço do servidor proxy (host:port) |
CURLOPT_PROXYTYPE |
Tipo: CURLPROXY_HTTP, CURLPROXY_SOCKS4, CURLPROXY_SOCKS5 |
CURLOPT_PROXYUSERPWD |
Login e senha no formato user:password |
CURLOPT_HTTPPROXYTUNNEL |
Ativar tunelamento através do HTTP CONNECT |
CURLOPT_SSL_VERIFYPEER |
Verificação do certificado SSL (false — desativar) |
CURLOPT_TIMEOUT |
Timeout da requisição em segundos |
CURLOPT_CONNECTTIMEOUT |
Timeout de conexão ao proxy |
Autenticação e SOCKS5 em cURL
A maioria dos proxies comerciais requer autenticação por login e senha. Além disso, SOCKS5 é o protocolo preferido quando é necessária total anonimidade, pois não adiciona cabeçalhos como X-Forwarded-For, que podem revelar o uso de proxy.
Proxy HTTP com login e senha
<?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 com autenticação
CURLOPT_PROXY => "$proxyHost:$proxyPort",
CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
CURLOPT_PROXYUSERPWD => "$proxyUser:$proxyPass",
// Tunelamento para HTTPS
CURLOPT_HTTPPROXYTUNNEL => true,
// Cabeçalhos do navegador para disfarce
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 'Erro cURL: ' . curl_error($ch);
} else {
echo $response;
}
curl_close($ch);
Proxy SOCKS5 em 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 com resolução de DNS através do proxy (recomendado!)
CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
// Autenticação (se necessário)
CURLOPT_PROXYUSERPWD => 'login:password',
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
💡 Importante: SOCKS5 vs SOCKS5_HOSTNAME
Use CURLPROXY_SOCKS5_HOSTNAME em vez de CURLPROXY_SOCKS5. A diferença é que com SOCKS5_HOSTNAME as requisições DNS também passam pelo proxy, o que evita vazamentos de DNS e aumenta a anonimidade. Com SOCKS5 normal, o DNS é resolvido localmente — isso pode revelar seu IP real.
Rotação de proxy em cURL
Um único proxy rapidamente será bloqueado ao fazer requisições em massa. A estratégia correta é ter um pool de proxies e alterná-los. Aqui está uma implementação simples, mas funcional:
<?php
class ProxyRotator
{
private array $proxies;
private int $currentIndex = 0;
public function __construct(array $proxies)
{
$this->proxies = $proxies;
shuffle($this->proxies); // Embaralha para ordem aleatória
}
/**
* Obter o próximo proxy do pool
*/
public function getNext(): string
{
$proxy = $this->proxies[$this->currentIndex];
$this->currentIndex = ($this->currentIndex + 1) % count($this->proxies);
return $proxy;
}
/**
* Obter um proxy aleatório
*/
public function getRandom(): string
{
return $this->proxies[array_rand($this->proxies)];
}
}
// Lista de proxies no 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);
/**
* Função de requisição com seleção automática de proxy
*/
function fetchWithProxy(string $url, ProxyRotator $rotator): ?string
{
$proxyStr = $rotator->getNext();
// Faz o parsing da string do 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 indisponível: $error");
return null;
}
return $response;
}
// Exemplo: scraping de 10 páginas com rotação de proxies
$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 "Recebido: " . strlen($html) . " bytes\n";
}
usleep(500000); // Pausa de 0.5 seg entre requisições
}
Note a usleep(500000) — a pausa entre requisições é criticamente importante. Mesmo com rotação de proxies, requisições muito frequentes podem levar ao bloqueio por padrões de comportamento. O intervalo recomendado é de 500 ms a 2 segundos, dependendo do site.
Proxy em Guzzle HTTP Client: configuração básica
Guzzle é uma poderosa biblioteca PHP para requisições HTTP, que é usada na maioria dos modernos frameworks PHP (Laravel, Symfony). Ela oferece uma API mais conveniente e legível em comparação com o cURL puro, suporta requisições assíncronas, middleware e um tratamento de erros conveniente.
Instalação via Composer
composer require guzzlehttp/guzzle
Proxy HTTP sem autenticação
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
// URL base (opcional)
'base_uri' => 'https://httpbin.org',
// Configurações padrão para todas as requisições
'timeout' => 30,
'connect_timeout' => 10,
// Proxy para todas as requisições do cliente
'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"}
No Guzzle, o proxy é definido através da opção proxy no formato URL. Isso é mais intuitivo do que no cURL. O proxy pode ser definido tanto no nível do cliente (para todas as requisições) quanto para cada requisição individualmente.
Proxy HTTP com autenticação
<?php
use GuzzleHttp\Client;
$login = 'your_login';
$password = 'your_password';
$host = '185.199.100.1';
$port = '8080';
$client = new Client([
'timeout' => 30,
]);
// Proxy com autenticação é passado no 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();
O formato do URL do proxy no Guzzle é: scheme://user:password@host:port. Para proxies HTTP, usamos http://, para SOCKS5 — socks5://.
Trabalho avançado com Guzzle: timeouts, cabeçalhos, SOCKS5
Vamos considerar cenários mais complexos: diferentes proxies para HTTP e HTTPS, SOCKS5, desativação da verificação SSL e configuração de cabeçalhos para simular um navegador.
Diferentes proxies para HTTP e HTTPS
<?php
use GuzzleHttp\Client;
$client = new Client([
'timeout' => 30,
'proxy' => [
// Proxy para requisições HTTP
'http' => 'http://login:[email protected]:8080',
// Proxy para requisições HTTPS
'https' => 'http://login:[email protected]:8080',
// Exceções — esses hosts vão sem proxy
'no' => ['localhost', '127.0.0.1', '.internal.corp'],
],
]);
SOCKS5 no Guzzle
<?php
use GuzzleHttp\Client;
// Para SOCKS5 no Guzzle, é necessário um pacote adicional
// composer require clue/socks-react (ou use o 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();
Configuração completa com cabeçalhos de navegador
<?php
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
$client = new Client([
'timeout' => 30,
'connect_timeout' => 10,
'verify' => false, // Desativar verificação SSL (para depuração)
'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 "Status: $statusCode, Tamanho: " . strlen($body) . " bytes\n";
⚠️ Sobre verify => false
Desativar a verificação SSL ('verify' => false) é aceitável apenas para depuração ou quando se trabalha com serviços internos. No código de produção, sempre mantenha a verificação SSL ativada, caso contrário, você estará vulnerável a ataques do tipo man-in-the-middle.
Rotação de proxy em Guzzle
No Guzzle, a rotação pode ser convenientemente implementada através de middleware — uma camada intermediária que intercepta cada requisição e adiciona a ela um proxy. Isso é mais elegante do que passar o proxy manualmente em cada chamada.
<?php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
/**
* Middleware para rotação automática de proxies
*/
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++;
// Adiciona o proxy nos atributos da requisição
// (passa através das opções no handler)
return $request->withHeader('X-Selected-Proxy', $proxy);
}
);
}
// Lista de proxies
$proxies = [
'http://user1:[email protected]:8080',
'http://user2:[email protected]:8080',
'http://user3:[email protected]:8080',
];
// Variante simples: classe wrapper sobre Guzzle com rotação
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);
}
}
// Uso
$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 "Erro: " . $e->getMessage() . "\n";
}
}
Tratamento de erros e depuração
Ao trabalhar com proxies, erros são inevitáveis: o proxy pode estar indisponível, responder lentamente, ou o site de destino pode retornar um erro. É importante tratar todas essas situações adequadamente.
Tratamento de erros em 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);
// Códigos de erro cURL para proxies
$proxyErrors = [
CURLE_COULDNT_CONNECT => 'Não foi possível conectar ao proxy',
CURLE_OPERATION_TIMEDOUT => 'Timeout de conexão ao proxy',
CURLE_RECV_ERROR => 'Erro ao receber dados',
CURLE_SSL_CONNECT_ERROR => 'Erro SSL através do proxy',
];
if ($errno) {
$message = $proxyErrors[$errno] ?? "Erro 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),
];
}
// Uso com tentativas repetidas
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 "Sucesso através de $proxy (tentativa $attempt, {$result['time']}s)\n";
return $result['body'];
}
echo "Erro através de $proxy: {$result['error']}\n";
if ($attempt < $maxRetries) {
sleep(2); // Pausa antes de repetir
}
}
}
return null; // Todos os proxies falharam
}
Tratamento de erros em 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 indisponível ou timeout de conexão
echo "Proxy indisponível ($proxy): " . $e->getMessage() . "\n";
} catch (ClientException $e) {
// HTTP 4xx — erro do cliente (403, 404, 429)
$code = $e->getResponse()->getStatusCode();
echo "HTTP $code para $url através de $proxy\n";
if ($code === 429) {
echo "Limite de taxa — pausa de 5 segundos\n";
sleep(5);
}
} catch (ServerException $e) {
// HTTP 5xx — erro do servidor
echo "Erro do servidor: " . $e->getResponse()->getStatusCode() . "\n";
} catch (RequestException $e) {
// Outros erros de requisição
echo "Erro de requisição: " . $e->getMessage() . "\n";
}
}
return null;
}
cURL vs Guzzle: o que escolher
Ambas as ferramentas funcionam bem com proxies, mas têm diferentes pontos fortes. Aqui está uma comparação com base em critérios-chave:
| Critério | cURL | Guzzle |
|---|---|---|
| Instalação | ✅ Integrado ao PHP | ⚠️ Necessita Composer |
| Legibilidade do código | ⚠️ Muitas opções, prolixo | ✅ API limpa e conveniente |
| Desempenho | ✅ Um pouco mais rápido (sem wrappers) | ✅ Comparável, tem async |
| Requisições assíncronas | ⚠️ curl_multi (difícil) | ✅ Suporte embutido |
| Middleware / hooks | ❌ Não | ✅ HandlerStack, Middleware |
| Suporte a SOCKS5 | ✅ Nativo | ✅ Através do curl handler |
| Tratamento de erros | ⚠️ Códigos de erro manualmente | ✅ Exceções com tipos |
| Integração em Laravel/Symfony | ⚠️ Manualmente | ✅ Suporte nativo |
| Adequado para | Scripts simples, sem dependências | Projetos em frameworks, lógica complexa |
Recomendação: Se você está escrevendo um script simples ou trabalhando em um servidor sem Composer — use cURL. Para projetos em Laravel, Symfony ou qualquer aplicação PHP moderna — Guzzle será significativamente mais conveniente e oferecerá mais possibilidades de escalabilidade.
🚀 Checklist rápido antes de iniciar
- ✅ Verifique se o proxy está funcionando:
curl -x http://login:pass@host:port https://httpbin.org/ip - ✅ Defina timeouts:
timeouteconnect_timeout - ✅ Adicione um User-Agent realista
- ✅ Implemente pausas entre requisições (mínimo 500 ms)
- ✅ Preveja tratamento de erros e fallback para outro proxy
- ✅ Para SOCKS5, use CURLPROXY_SOCKS5_HOSTNAME (não SOCKS5)
- ✅ Não desative a verificação SSL em produção
Conclusão
Configurar proxy em PHP é uma tarefa simples, se você souber as opções e formatos corretos. Para tarefas básicas, basta usar cURL com CURLOPT_PROXY e CURLOPT_PROXYUSERPWD. Para projetos em frameworks, Guzzle oferece uma API mais conveniente, exceções tipadas e suporte a middleware para rotação de proxies.
Princípios-chave para um funcionamento confiável: use SOCKS5 com resolução de DNS através do proxy (CURLPROXY_SOCKS5_HOSTNAME), sempre defina timeouts, adicione pausas entre requisições e implemente rotação de proxies para tarefas em larga escala. O tratamento de erros com fallback para outro proxy é um elemento obrigatório em qualquer código de produção.
Se você seguir essas diretrizes, estará bem preparado para trabalhar com proxies em PHP de forma eficaz e segura.
```