Kembali ke blog

Pengaturan Proxy di Go (Golang): http.Client, SOCKS5, dan Rotasi IP untuk Pengambilan Data dan Automasi

Cara mengatur proxy server di Go melalui http.Client - dari konfigurasi dasar hingga rotasi IP dan penggunaan SOCKS5. Panduan lengkap dengan contoh kode.

📅11 Agustus 2026
```html

Jika Anda menulis pengurai, mengotomatiskan pengumpulan data, atau menguji API dari berbagai wilayah — cepat atau lambat Anda akan menghadapi pemblokiran berdasarkan IP. Go adalah salah satu bahasa yang paling populer untuk menulis alat jaringan berkinerja tinggi, dan pengaturan proksi di sini memiliki keunikan tersendiri. Dalam panduan ini, kita akan membahas semuanya dari konfigurasi dasar http.Client hingga rotasi IP tingkat lanjut dengan dukungan SOCKS5.

Mengapa proksi diperlukan dalam aplikasi Go

Go telah menjadi standar de facto untuk menulis alat jaringan yang sangat terbebani: pengurai, perayap, bot untuk pemantauan harga, alat otomatisasi API. Di sinilah kebutuhan akan proksi paling sering muncul — dan di sinilah penting untuk mengatur semuanya dengan benar dari awal.

Skenario utama di mana proksi tidak dapat dihindari:

  • Pemrosesan pasar — Wildberries, Ozon, AliExpress memblokir IP setelah 50–200 permintaan dari satu alamat. Tanpa pergantian IP, pengurai akan berhenti.
  • Pengujian geo — memeriksa hasil pencarian mesin pencari, iklan, atau harga dari berbagai negara dan wilayah.
  • Menghindari batasan laju — distribusi beban di beberapa IP memungkinkan untuk menghindari batasan jumlah permintaan per menit.
  • Anonymitas — menyembunyikan IP nyata server saat bekerja dengan API eksternal.
  • Pengujian CDN dan geolokasi — memeriksa keakuratan penyajian konten untuk berbagai wilayah.

Pustaka standar Go menyediakan alat yang fleksibel untuk bekerja dengan proksi — melalui http.Transport dan http.Client. Mari kita bahas setiap cara secara mendetail.

Pengaturan dasar HTTP-proksi melalui http.Client

Di Go, proksi diatur di tingkat http.Transport — lapisan transport HTTP-client. Ini memberikan kontrol penuh atas proksi mana yang digunakan untuk permintaan, dan memungkinkan untuk membuat beberapa klien dengan proksi yang berbeda secara bersamaan.

Contoh kerja minimal untuk pengaturan HTTP-proksi:

package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
)

func main() {
    // Menentukan alamat server proksi
    proxyURL, err := url.Parse("http://123.45.67.89:8080")
    if err != nil {
        panic(err)
    }

    // Membuat transport dengan proksi
    transport := &http.Transport{
        Proxy: http.ProxyURL(proxyURL),
    }

    // Membuat klien dengan transport kita
    client := &http.Client{
        Transport: transport,
    }

    // Melakukan permintaan melalui proksi
    resp, err := client.Get("https://httpbin.org/ip")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Apa yang terjadi dalam kode ini:

  1. url.Parse — mengurai string dengan alamat proksi menjadi struktur *url.URL
  2. http.Transport{Proxy: http.ProxyURL(proxyURL)} — membuat transport yang mengarahkan semua permintaan melalui proksi yang ditentukan
  3. http.Client{Transport: transport} — membuat klien dengan transport kita

Poin penting: jika Anda menggunakan http.DefaultClient (yaitu hanya http.Get() tanpa klien eksplisit), proksi tidak akan diterapkan. Selalu buat http.Client sendiri untuk bekerja dengan proksi.

💡 Tips

Tambahkan timeout di http.Client: Timeout: 30 * time.Second. Server proksi mungkin merespons lebih lambat daripada koneksi langsung, dan tanpa timeout, goroutine akan menggantung selamanya.

Proksi melalui variabel lingkungan

Go mendukung variabel lingkungan standar untuk proksi — HTTP_PROXY, HTTPS_PROXY, dan NO_PROXY. Transport standar http.DefaultTransport membacanya secara otomatis melalui fungsi http.ProxyFromEnvironment.

Mengatur variabel lingkungan sebelum menjalankan aplikasi:

# Di terminal (Linux/macOS)
export HTTP_PROXY="http://123.45.67.89:8080"
export HTTPS_PROXY="http://123.45.67.89:8080"
export NO_PROXY="localhost,127.0.0.1"

# Menjalankan aplikasi
go run main.go

Jika Anda ingin menggunakan ProxyFromEnvironment dalam transport Anda sendiri (misalnya, dengan pengaturan TLS tambahan), lakukan seperti ini:

transport := &http.Transport{
    Proxy: http.ProxyFromEnvironment,
    // Pengaturan tambahan...
    TLSHandshakeTimeout:   10 * time.Second,
    ResponseHeaderTimeout: 30 * time.Second,
}

client := &http.Client{
    Transport: transport,
    Timeout:   60 * time.Second,
}

Pendekatan ini nyaman untuk lingkungan produksi, di mana alamat proksi ditentukan melalui konfigurasi penyebaran (Docker, Kubernetes, systemd), dan bukan di-hardcode dalam kode. Ini juga memudahkan penggantian proksi tanpa perlu mengkompilasi ulang aplikasi.

Menghubungkan proksi SOCKS5 di Golang

SOCKS5 adalah protokol yang lebih universal dibandingkan dengan proksi HTTP. Ini bekerja pada tingkat yang lebih rendah dan mendukung semua koneksi TCP/UDP, bukan hanya HTTP/HTTPS. Untuk bekerja dengan pengurai dan otomatisasi, SOCKS5 sering kali lebih disukai — terutama saat menggunakan proksi residensial, yang paling sering disediakan melalui protokol ini.

Untuk bekerja dengan SOCKS5 di Go, Anda memerlukan paket golang.org/x/net/proxy:

go get golang.org/x/net/proxy

Contoh menghubungkan proksi SOCKS5 tanpa autentikasi:

package main

import (
    "fmt"
    "io"
    "net/http"
    "golang.org/x/net/proxy"
)

func main() {
    // Membuat dialer SOCKS5
    dialer, err := proxy.SOCKS5("tcp", "123.45.67.89:1080", nil, proxy.Direct)
    if err != nil {
        panic(err)
    }

    // Membuat transport dengan dialer kustom
    transport := &http.Transport{
        Dial: dialer.Dial,
    }

    client := &http.Client{
        Transport: transport,
    }

    resp, err := client.Get("https://httpbin.org/ip")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Mulai dari Go 1.20+, Anda juga dapat menggunakan dukungan bawaan SOCKS5 melalui url.Parse dengan awalan socks5://:

proxyURL, _ := url.Parse("socks5://123.45.67.89:1080")

transport := &http.Transport{
    Proxy: http.ProxyURL(proxyURL),
}

client := &http.Client{Transport: transport}

Sintaksis ini lebih sederhana dan tidak memerlukan ketergantungan tambahan. Namun, untuk skenario yang lebih kompleks (misalnya, SOCKS5 dengan autentikasi di versi Go yang lebih lama), paket golang.org/x/net/proxy tetap relevan.

Autentikasi: login dan kata sandi di proksi

Sebagian besar penyedia proksi komersial menggunakan autentikasi dengan login dan kata sandi. Ada dua cara untuk mengirimkan kredensial di Go.

Cara 1: Mengintegrasikan ke dalam URL

// Format: protocol://username:password@host:port
proxyURL, err := url.Parse("http://myuser:[email protected]:8080")
if err != nil {
    panic(err)
}

transport := &http.Transport{
    Proxy: http.ProxyURL(proxyURL),
}

client := &http.Client{
    Transport: transport,
    Timeout:   30 * time.Second,
}

Cara 2: Melalui struktur url.URL (lebih aman)

proxyURL := &url.URL{
    Scheme: "http",
    Host:   "proxy.example.com:8080",
    User:   url.UserPassword("myuser", "mypassword"),
}

transport := &http.Transport{
    Proxy: http.ProxyURL(proxyURL),
}

client := &http.Client{
    Transport: transport,
    Timeout:   30 * time.Second,
}

Cara kedua lebih disukai — ia menangani karakter khusus dalam kata sandi dengan benar (misalnya, @, #, :), yang dapat merusak penguraian URL dalam opsi pertama.

Untuk SOCKS5 dengan autentikasi melalui paket golang.org/x/net/proxy:

auth := &proxy.Auth{
    User:     "myuser",
    Password: "mypassword",
}

dialer, err := proxy.SOCKS5("tcp", "proxy.example.com:1080", auth, proxy.Direct)
if err != nil {
    panic(err)
}

transport := &http.Transport{
    Dial: dialer.Dial,
}

client := &http.Client{
    Transport: transport,
    Timeout:   30 * time.Second,
}

⚠️ Keamanan

Jangan pernah meng-hardcode login dan kata sandi proksi langsung dalam kode. Gunakan variabel lingkungan atau file konfigurasi yang tidak masuk ke repositori. Tambahkan .env ke .gitignore.

Rotasi proksi: beralih IP antara permintaan

Satu proksi — baik untuk pengujian, tetapi untuk penguraian nyata dibutuhkan rotasi IP. Situs web melacak frekuensi permintaan dari satu alamat, dan bahkan proksi yang baik akan diblokir jika mengirimkan ribuan permintaan berturut-turut.

Ada dua pendekatan untuk rotasi di Go:

Pendekatan 1: Daftar proksi dengan round-robin

package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "sync/atomic"
    "time"
)

type ProxyRotator struct {
    proxies []*url.URL
    counter uint64
}

func NewProxyRotator(proxyURLs []string) (*ProxyRotator, error) {
    proxies := make([]*url.URL, 0, len(proxyURLs))
    for _, p := range proxyURLs {
        u, err := url.Parse(p)
        if err != nil {
            return nil, err
        }
        proxies = append(proxies, u)
    }
    return &ProxyRotator{proxies: proxies}, nil
}

// GetClient mengembalikan klien dengan proksi berikutnya secara melingkar
func (r *ProxyRotator) GetClient() *http.Client {
    idx := atomic.AddUint64(&r.counter, 1) % uint64(len(r.proxies))
    proxyURL := r.proxies[idx]

    transport := &http.Transport{
        Proxy: http.ProxyURL(proxyURL),
    }

    return &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second,
    }
}

func main() {
    proxyList := []string{
        "http://user:[email protected]:8080",
        "http://user:[email protected]:8080",
        "http://user:[email protected]:8080",
    }

    rotator, err := NewProxyRotator(proxyList)
    if err != nil {
        panic(err)
    }

    // Setiap permintaan melalui proksi berikutnya
    for i := 0; i < 10; i++ {
        client := rotator.GetClient()
        resp, err := client.Get("https://httpbin.org/ip")
        if err != nil {
            fmt.Printf("Kesalahan permintaan %d: %v\n", i, err)
            continue
        }
        body, _ := io.ReadAll(resp.Body)
        resp.Body.Close()
        fmt.Printf("Permintaan %d: %s\n", i, string(body))
    }
}

Pendekatan 2: Pemilihan proksi dinamis melalui fungsi

Bidang Proxy dalam http.Transport menerima fungsi dengan tanda tangan func(*http.Request) (*url.URL, error). Ini memungkinkan pemilihan proksi secara dinamis untuk setiap permintaan:

proxies := []*url.URL{
    mustParseURL("http://proxy1.example.com:8080"),
    mustParseURL("http://proxy2.example.com:8080"),
    mustParseURL("http://proxy3.example.com:8080"),
}

var counter uint64

transport := &http.Transport{
    Proxy: func(req *http.Request) (*url.URL, error) {
        // Memilih proksi berdasarkan permintaan (round-robin)
        idx := atomic.AddUint64(&counter, 1) % uint64(len(proxies))
        return proxies[idx], nil
    },
}

client := &http.Client{
    Transport: transport,
    Timeout:   30 * time.Second,
}

func mustParseURL(s string) *url.URL {
    u, err := url.Parse(s)
    if err != nil {
        panic(err)
    }
    return u
}

Pendekatan kedua lebih elegan — satu klien secara otomatis merotasi proksi untuk setiap permintaan. Ini sangat nyaman untuk permintaan bersamaan melalui goroutine.

Jenis proksi apa yang harus dipilih untuk proyek Go

Pemilihan jenis proksi secara langsung mempengaruhi keberhasilan proyek Anda. Untuk berbagai tugas, jenis yang berbeda cocok — mari kita bahas opsi utama:

Jenis proksi Kecepatan Anonymitas Terbaik untuk
Data-center ⚡ Tinggi Sedang Penguraian data terbuka, pengujian API, pengujian beban
Residen 🔄 Sedang Tinggi Penguraian pasar (Wildberries, Ozon), menghindari perlindungan anti-bot
Mobile 🔄 Sedang Maksimal Bekerja dengan media sosial, layanan dengan perlindungan anti-bot yang ketat

Untuk penguraian pasar (Wildberries, Ozon, Avito): gunakan proksi residensial. Platform-platform ini secara aktif menggunakan sistem deteksi bot (Cloudflare, solusi internal) dan dengan mudah memblokir IP dari data-center. IP residensial terlihat seperti pengguna rumah biasa.

Untuk pengujian API dan pengujian beban: proksi data-center adalah pilihan optimal. Kecepatan tinggi, koneksi stabil, dan latensi yang dapat diprediksi.

Untuk pengujian geo: proksi residensial dengan pemilihan negara/kota. Memungkinkan Anda memeriksa bagaimana layanan atau iklan Anda terlihat bagi pengguna dari wilayah tertentu.

Kesalahan umum dan praktik terbaik

Selama bertahun-tahun bekerja dengan proksi dalam proyek Go, telah terbentuk daftar kesalahan umum yang memperlambat pengembangan dan mengurangi keandalan pengurai. Mari kita bahas masing-masing.

Kesalahan 1: Menggunakan transport tanpa mereset koneksi

http.Transport menyimpan koneksi TCP (keep-alive). Jika Anda membuat transport baru untuk setiap permintaan, Anda kehilangan keuntungan ini. Namun, jika Anda menggunakan satu transport dengan proksi tetap saat rotasi — permintaan dapat melalui koneksi lama, melewati proksi baru.

Solusi: saat mengganti proksi, panggil transport.CloseIdleConnections() atau gunakan pendekatan fungsional dengan pemilihan proksi dinamis (yang dijelaskan di bagian rotasi).

Kesalahan 2: Tidak ada penanganan kesalahan proksi

Proksi mungkin tidak tersedia, kelebihan beban, atau diblokir oleh situs target. Tanpa logika retry, pengurai Anda akan berhenti pada kesalahan pertama.

func fetchWithRetry(client *http.Client, targetURL string, maxRetries int) ([]byte, error) {
    var lastErr error
    for i := 0; i < maxRetries; i++ {
        resp, err := client.Get(targetURL)
        if err != nil {
            lastErr = err
            time.Sleep(time.Duration(i+1) * time.Second) // Penundaan eksponensial
            continue
        }
        defer resp.Body.Close()

        if resp.StatusCode == 429 || resp.StatusCode == 403 {
            lastErr = fmt.Errorf("diblokir: status %d", resp.StatusCode)
            time.Sleep(time.Duration(i+1) * 2 * time.Second)
            continue
        }

        return io.ReadAll(resp.Body)
    }
    return nil, fmt.Errorf("semua percobaan habis: %w", lastErr)
}

Kesalahan 3: Mengabaikan verifikasi TLS

Beberapa pengembang menonaktifkan verifikasi sertifikat TLS untuk proksi (InsecureSkipVerify: true). Ini menciptakan kerentanan terhadap serangan MITM. Jika server proksi menggunakan sertifikat yang ditandatangani sendiri — tambahkan ke dalam kumpulan yang dipercaya, bukan menonaktifkan verifikasi sepenuhnya.

Kesalahan 4: Header yang sama untuk semua permintaan

Bahkan dengan rotasi proksi, Anda dapat diblokir berdasarkan pola header. Ubah User-Agent bersama dengan proksi, tambahkan header browser yang realistis:

userAgents := []string{
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/119.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/118.0.0.0 Safari/537.36",
}

req, _ := http.NewRequest("GET", targetURL, nil)
req.Header.Set("User-Agent", userAgents[rand.Intn(len(userAgents))])
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
req.Header.Set("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7")
req.Header.Set("Accept-Encoding", "gzip, deflate, br")

Daftar periksa klien proksi yang andal di Go

  • ✅ Timeout pada klien (Timeout: 30 * time.Second)
  • ✅ Logika retry dengan penundaan eksponensial
  • ✅ Rotasi proksi pada kesalahan 403/429
  • ✅ Rotasi User-Agent bersama dengan proksi
  • ✅ Pencatatan kesalahan dengan menyebutkan proksi yang digunakan
  • ✅ Kredensial melalui variabel lingkungan, tidak di-hardcode
  • ✅ Menutup resp.Body melalui defer
  • ✅ Pembatasan konkurensi melalui semaphore atau worker pool

Contoh pool pekerja lengkap dengan rotasi proksi

package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "sync"
    "sync/atomic"
    "time"
)

type Scraper struct {
    proxies []*url.URL
    counter uint64
    workers int
}

func (s *Scraper) getClient() *http.Client {
    idx := atomic.AddUint64(&s.counter, 1) % uint64(len(s.proxies))
    return &http.Client{
        Transport: &http.Transport{
            Proxy: http.ProxyURL(s.proxies[idx]),
        },
        Timeout: 30 * time.Second,
    }
}

func (s *Scraper) Scrape(urls []string) map[string]string {
    results := make(map[string]string)
    var mu sync.Mutex
    var wg sync.WaitGroup

    sem := make(chan struct{}, s.workers) // Semaphore untuk membatasi konkurensi

    for _, u := range urls {
        wg.Add(1)
        go func(targetURL string) {
            defer wg.Done()
            sem <- struct{}{}
            defer func() { <-sem }()

            client := s.getClient()
            resp, err := client.Get(targetURL)
            if err != nil {
                fmt.Printf("Kesalahan: %v\n", err)
                return
            }
            defer resp.Body.Close()

            body, _ := io.ReadAll(resp.Body)

            mu.Lock()
            results[targetURL] = string(body)
            mu.Unlock()
        }(u)
    }

    wg.Wait()
    return results
}

Kesimpulan

Mengatur proksi di Go adalah tugas yang dapat diselesaikan dengan beberapa baris kode, tetapi memerlukan pemahaman tentang nuansa: penggunaan yang benar dari http.Transport, penanganan autentikasi yang benar, rotasi IP yang cerdas, dan penanganan kesalahan. Pustaka standar menyediakan semua yang diperlukan untuk proksi HTTP, dan paket golang.org/x/net/proxy memenuhi kebutuhan untuk SOCKS5.

Kesimpulan kunci dari artikel ini:

  • Selalu buat http.Client sendiri — http.DefaultClient tidak mendukung proksi secara langsung
  • Gunakan pendekatan fungsional (Proxy: func(r *Request) (*url.URL, error)) untuk rotasi dinamis
  • Simpan kredensial dalam variabel lingkungan, bukan dalam kode
  • Tambahkan logika retry — proksi tidak selalu 100% dapat diandalkan
  • Ubah User-Agent bersama dengan proksi untuk efisiensi maksimal

Jika Anda menulis pengurai untuk pasar seperti Wildberries atau Ozon, kami merekomendasikan menggunakan proksi residensial — mereka memiliki IP nyata dari pengguna rumah dan jauh lebih jarang terkena pemblokiran oleh sistem anti-bot. Untuk skenario dengan beban tinggi dengan ribuan permintaan per menit, pilihan optimal adalah proksi data-center — mereka memberikan kecepatan dan stabilitas koneksi maksimum.

```