Panduan5 menit baca

Proxy di PHP: cURL, Guzzle, Laravel, dan sesi sticky

Kode siap tempel untuk pakai residential proxy di PHP: cURL, SOCKS5, Guzzle, HTTP client Laravel, sesi sticky, retry, dan cara hemat kuota GB.

Kalau skrip PHP-mu perlu mengambil halaman publik dari IP Indonesia atau negara lain, bagian proxy-nya cukup beberapa baris. Panduan ini memakai ekstensi cURL bawaan PHP, lalu Guzzle dan HTTP client Laravel, lengkap dengan sesi sticky, retry, dan cara supaya kuota GB tidak cepat habis.

Siapkan kredensial dan format username

Semua protokol (HTTP, HTTPS, SOCKS5) memakai satu endpoint, proxy.waroengproxy.com:8000. Username dan password ada di kartu Kredensial proxy di dashboard. Belum punya akun? Buat akun dulu, isi saldo lewat QRIS, lalu beli GB sesuai kebutuhan di halaman harga.

Pengaturan negara, kota, dan sesi ditulis langsung di username, dipisah tanda minus. Contoh: USERNAME-cc-ID untuk IP Indonesia, atau USERNAME-cc-ID-s-sesi1-ttl-600 untuk IP yang sama selama 600 detik. Daftar lengkap parameternya ada di dokumentasi, dan Generator proxy bisa merakitkan baris siap pakai.

Permintaan pertama dengan ekstensi cURL

Ekstensi cURL sudah aktif di hampir semua instalasi PHP (cek dengan php -m). Tempel kode berikut ke file, ganti USERNAME dan PASSWORD, lalu jalankan dengan php tes.php.

Contoh
<?php
$ch = curl_init('https://ipinfo.io/json');
curl_setopt_array($ch, [
CURLOPT_PROXY => 'proxy.waroengproxy.com:8000',
CURLOPT_PROXYUSERPWD => 'USERNAME-cc-ID:PASSWORD',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 45,
]);
$body = curl_exec($ch);
if ($body === false) {
echo 'cURL error: ' . curl_error($ch) . PHP_EOL;
echo 'Proxy CONNECT code: ' . curl_getinfo($ch, CURLINFO_HTTP_CONNECTCODE) . PHP_EOL;
} else {
echo curl_getinfo($ch, CURLINFO_HTTP_CODE) . PHP_EOL . $body . PHP_EOL;
}

Respons ipinfo.io menampilkan IP, kota, dan jaringan yang dilihat situs tujuan. Untuk alamat https, cURL otomatis membuka terowongan CONNECT lewat proxy, jadi TLS tetap ujung ke ujung antara skripmu dan situs tujuan. Kamu tidak perlu mengatur CURLOPT_HTTPPROXYTUNNEL untuk ini.

Kalau curl_exec mengembalikan false, lihat dua hal: pesan curl_error dan CURLINFO_HTTP_CONNECTCODE. Kode 407 berarti username atau password ditolak proxy; penyebab umumnya ada di panduan error proxy.

Pakai SOCKS5 tanpa bocor DNS

Beberapa aplikasi lebih cocok lewat SOCKS5. Di PHP cukup ganti skema atau tipe proxy. Pakai socks5h:// (atau CURLPROXY_SOCKS5_HOSTNAME), bukan socks5://, supaya nama domain di-resolve di sisi proxy. Dengan socks5:// biasa, server tempat skripmu berjalan yang melakukan lookup DNS, sehingga resolver lokalmu ikut tahu domain apa yang kamu buka.

Contoh
// SOCKS5, DNS resolved on the proxy side
curl_setopt($ch, CURLOPT_PROXY, 'socks5h://proxy.waroengproxy.com:8000');
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'USERNAME-cc-ID:PASSWORD');
// equivalent: host:port + proxy type
curl_setopt($ch, CURLOPT_PROXY, 'proxy.waroengproxy.com:8000');
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);

Rotating atau sticky: atur lewat username

Tanpa parameter -s-, proxy berjalan dalam mode rotating dan IP bisa berganti. Perhatikan satu hal di cURL: kalau kamu memakai ulang handle yang sama, koneksinya juga dipakai ulang. Untuk alamat https, permintaan berikutnya lewat terowongan yang sama sehingga IP-nya tetap. Butuh IP baru per permintaan? Buat handle baru, atau set CURLOPT_FRESH_CONNECT ke true.

Sebaliknya, kalau alurnya perlu IP tetap (misalnya beberapa halaman berurutan di situs yang sama), pakai sesi sticky. Label sesi yang sama berarti IP yang sama, dan ttl menentukan lamanya dalam detik (1 sampai 86400, bawaan 60). Label tidak boleh berisi tanda minus karena tanda itu pemisah parameter.

Contoh
<?php
function proxyUser(string $base, string $cc, ?string $session = null, int $ttl = 600): string
{
$user = "{$base}-cc-{$cc}";
if ($session !== null) {
$user .= "-s-{$session}-ttl-{$ttl}";
}
return $user;
}
$sid = bin2hex(random_bytes(5)); // 10 hex chars, no minus sign
$user = proxyUser('USERNAME', 'ID', $sid, 600);
// USERNAME-cc-ID-s-<sid>-ttl-600
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_PROXY => 'proxy.waroengproxy.com:8000',
CURLOPT_PROXYUSERPWD => "{$user}:PASSWORD",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 45,
]);
foreach (['https://example.com/page-1', 'https://example.com/page-2'] as $url) {
curl_setopt($ch, CURLOPT_URL, $url);
$html = curl_exec($ch);
// ...
}

Untuk beberapa worker paralel, beri tiap worker label sesinya sendiri. Dengan begitu tiap worker punya IP stabil, tapi worker satu dan lainnya tidak berbagi IP.

Guzzle: satu opsi proxy

Kalau proyekmu sudah memakai Composer, Guzzle menerima proxy dalam bentuk URL di opsi proxy. Opsi ini bisa dipasang di client (berlaku untuk semua permintaan) atau di tiap permintaan untuk menimpa negara atau sesi.

Contoh
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$user = 'USERNAME-cc-ID';
$pass = rawurlencode('PASSWORD'); // encode special characters in the password
$proxy = "http://{$user}:{$pass}@proxy.waroengproxy.com:8000";
$client = new Client([
'proxy' => $proxy,
'connect_timeout' => 15,
'timeout' => 45,
]);
$res = $client->get('https://ipinfo.io/json');
echo $res->getStatusCode(), PHP_EOL, $res->getBody(), PHP_EOL;
// per-request override, e.g. a different country
$res = $client->get('https://ipinfo.io/json', [
'proxy' => "http://USERNAME-cc-SG:{$pass}@proxy.waroengproxy.com:8000",
]);

Password yang berisi karakter seperti @, :, atau / harus di-encode dengan rawurlencode saat ditaruh di URL, kalau tidak Guzzle akan salah membaca alamat proxy. Guzzle juga menerima array, misalnya ['http' => ..., 'https' => ..., 'no' => ['localhost']], kalau ada host yang tidak boleh lewat proxy. Skema socks5h:// bisa dipakai juga di sini selama Guzzle berjalan di atas handler cURL (bawaannya begitu).

HTTP client Laravel

Facade Http di Laravel dibangun di atas Guzzle, jadi opsi proxy diteruskan lewat withOptions. Simpan URL proxy lengkap di variabel lingkungan, petakan ke config/services.php, lalu baca lewat config() supaya tetap jalan saat konfigurasi di-cache.

Contoh
use Illuminate\Support\Facades\Http;
$res = Http::withOptions(['proxy' => config('services.proxy.url')])
->connectTimeout(15)
->timeout(45)
->retry(3, 2000, throw: false)
->get('https://ipinfo.io/json');
if ($res->successful()) {
$data = $res->json();
}
Jangan menulis password proxy langsung di kode yang masuk Git. Simpan di variabel lingkungan atau secret manager, dan kalau sempat bocor, ganti password dari dashboard.

Retry yang wajar dan hemat kuota

Retry membantu saat ada timeout sesaat, tapi retry yang membabi buta justru membuang GB. Ulangi hanya untuk kegagalan koneksi, 429, dan 5xx, dengan jeda yang makin panjang. Respons 403 atau 404 tidak akan berubah kalau diulang, jadi berhenti saja dan periksa penyebabnya.

Contoh
<?php
function fetch(string $url, string $userPass, int $tries = 3): ?string
{
for ($i = 1; $i <= $tries; $i++) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_PROXY => 'proxy.waroengproxy.com:8000',
CURLOPT_PROXYUSERPWD => $userPass,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '', // accept gzip/deflate/br
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 45,
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($body !== false && $code < 500 && $code !== 429) {
return $body; // 2xx, 3xx, 403, 404: done, no retry
}
sleep(2 ** $i); // 2, 4, 8 seconds
}
return null;
}
  • CURLOPT_ENCODING => '' meminta respons terkompresi (gzip, deflate, atau br sesuai dukungan libcurl), sehingga data yang lewat proxy lebih kecil.
  • Kalau cuma perlu tahu halaman masih ada, pakai CURLOPT_NOBODY untuk permintaan HEAD tanpa mengunduh isi.
  • Ambil endpoint JSON atau halaman ringan kalau tersedia, jangan unduh gambar, video, atau file besar yang tidak kamu proses.
  • Simpan hasil ke cache atau database supaya halaman yang sama tidak diambil berulang kali.

Estimasi berapa GB yang kamu butuhkan bisa dihitung dari ukuran rata-rata halaman; caranya ada di artikel berapa GB yang sebenarnya kamu butuhkan.

Proxy residential mengubah IP, bukan aturan main. Ambil hanya data publik, patuhi syarat layanan situs tujuan dan robots.txt, beri jeda antar permintaan, dan jangan membebani server orang lain. Pembahasan lebih lengkapnya ada di scraping yang sopan dan hemat kuota.

Mulai sekarang

Dengan kode di atas, skrip PHP-mu sudah bisa memilih negara, menahan IP lewat sesi sticky, dan berhenti mengulang saat memang tidak perlu. Pakai Python juga? Polanya sama di panduan Python requests. Siap mencoba? Buat akun, beli GB secukupnya, lalu rakit username lewat Generator proxy.