Voltar ao blog

Configuração de Proxy em Go (Golang): http.Client, SOCKS5 e Rotação de IP para Web Scraping e Automação

Como configurar corretamente um servidor proxy em Go através de http.Client — desde a configuração básica até a rotação de IP e trabalho com SOCKS5. Um guia completo com exemplos de código.

📅11 de agosto de 2026
```html

Se você está escrevendo um parser, automatizando a coleta de dados ou testando APIs de diferentes regiões — cedo ou tarde você encontrará bloqueios por IP. Go é uma das linguagens mais populares para escrever ferramentas de rede de alto desempenho, e a configuração de proxy aqui tem suas particularidades. Neste guia, vamos abordar tudo, desde a configuração básica do http.Client até a rotação avançada de IP com suporte a SOCKS5.

Por que usar proxies em aplicações Go

Go se tornou o padrão de fato para escrever ferramentas de rede de alta carga: parsers, crawlers, bots para monitoramento de preços, ferramentas de automação de API. É aqui que a necessidade de proxies surge com mais frequência — e é aqui que é importante configurar tudo corretamente desde o início.

Os principais cenários em que você não pode evitar proxies:

  • Scraping de marketplaces — Wildberries, Ozon, AliExpress bloqueiam IPs após 50–200 requisições de um único endereço. Sem troca de IP, o parser para.
  • Teste geográfico — verificação de resultados de mecanismos de busca, anúncios ou preços de diferentes países e regiões.
  • Contorno de rate-limit — distribuir a carga entre vários IPs permite contornar as limitações de requisições por minuto.
  • Anonimato — ocultar o IP real do servidor ao trabalhar com APIs externas.
  • Teste de CDN e geolocalização — verificar a correção da entrega de conteúdo para diferentes regiões.

A biblioteca padrão do Go fornece ferramentas flexíveis para trabalhar com proxies — através do http.Transport e http.Client. Vamos detalhar cada método.

Configuração básica de HTTP proxy através do http.Client

Em Go, o proxy é configurado no nível do http.Transport — camada de transporte do cliente HTTP. Isso dá controle total sobre qual proxy as requisições estão passando e permite criar vários clientes com diferentes proxies simultaneamente.

Um exemplo mínimo funcional de configuração de HTTP proxy:

package main

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

func main() {
    // Especificando o endereço do servidor proxy
    proxyURL, err := url.Parse("http://123.45.67.89:8080")
    if err != nil {
        panic(err)
    }

    // Criando um transporte com o proxy
    transport := &http.Transport{
        Proxy: http.ProxyURL(proxyURL),
    }

    // Criando um cliente com nosso transporte
    client := &http.Client{
        Transport: transport,
    }

    // Fazendo uma requisição através do proxy
    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))
}

O que acontece neste código:

  1. url.Parse — analisa a string com o endereço do proxy em uma estrutura *url.URL
  2. http.Transport{Proxy: http.ProxyURL(proxyURL)} — cria um transporte que direciona todas as requisições através do proxy especificado
  3. http.Client{Transport: transport} — cria um cliente com nosso transporte

Um ponto importante: se você usar http.DefaultClient (ou seja, apenas http.Get() sem um cliente explícito), o proxy não será aplicado. Sempre crie seu próprio http.Client para trabalhar com proxies.

💡 Dica

Adicione um timeout no http.Client: Timeout: 30 * time.Second. Servidores proxy podem responder mais lentamente do que uma conexão direta, e sem um timeout, as goroutines podem ficar presas indefinidamente.

Proxy através de variáveis de ambiente

Go suporta variáveis de ambiente padrão para proxies — HTTP_PROXY, HTTPS_PROXY e NO_PROXY. O transporte padrão http.DefaultTransport lê essas variáveis automaticamente através da função http.ProxyFromEnvironment.

Configurando variáveis de ambiente antes de executar o aplicativo:

# No 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"

# Executando o aplicativo
go run main.go

Se você quiser usar ProxyFromEnvironment em seu próprio transporte (por exemplo, com configurações adicionais de TLS), faça assim:

transport := &http.Transport{
    Proxy: http.ProxyFromEnvironment,
    // Configurações adicionais...
    TLSHandshakeTimeout:   10 * time.Second,
    ResponseHeaderTimeout: 30 * time.Second,
}

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

Essa abordagem é conveniente para ambientes de produção, onde o endereço do proxy é definido através da configuração de deployment (Docker, Kubernetes, systemd), e não é codificado diretamente no código. Isso também facilita a troca de proxies sem recompilar o aplicativo.

Conexão de proxy SOCKS5 em Golang

SOCKS5 é um protocolo mais versátil em comparação com proxies HTTP. Ele opera em um nível mais baixo e suporta qualquer conexão TCP/UDP, e não apenas HTTP/HTTPS. Para trabalhar com parsers e automação, SOCKS5 é frequentemente preferível — especialmente ao usar proxies residenciais, que geralmente são fornecidos exatamente por esse protocolo.

Para trabalhar com SOCKS5 em Go, você precisa do pacote golang.org/x/net/proxy:

go get golang.org/x/net/proxy

Exemplo de conexão de proxy SOCKS5 sem autenticação:

package main

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

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

    // Criando um transporte com o dialer personalizado
    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))
}

A partir do Go 1.20+, você também pode usar o suporte embutido a SOCKS5 através do url.Parse com o prefixo socks5://:

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

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

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

Essa sintaxe é mais simples e não requer dependências adicionais. No entanto, para cenários mais complexos (por exemplo, SOCKS5 com autenticação em versões mais antigas do Go), o pacote golang.org/x/net/proxy ainda é relevante.

Autenticação: login e senha no proxy

A maioria dos provedores de proxies comerciais utiliza autenticação por login e senha. Existem duas maneiras de passar as credenciais em Go.

Método 1: Embutir na URL

// Formato: 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,
}

Método 2: Através da estrutura url.URL (mais seguro)

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,
}

O segundo método é preferível — ele trata corretamente caracteres especiais na senha (por exemplo, @, #, :), que podem quebrar a análise da URL na primeira opção.

Para SOCKS5 com autenticação através do pacote 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,
}

⚠️ Segurança

Nunca codifique o login e a senha do proxy diretamente no código. Use variáveis de ambiente ou arquivos de configuração que não sejam incluídos no repositório. Adicione .env ao .gitignore.

Rotação de proxies: alternando IP entre requisições

Um proxy é bom para testes, mas para scraping real você precisa de rotação de IP. Sites monitoram a frequência de requisições de um único endereço, e até mesmo um bom proxy será bloqueado se você enviar milhares de requisições consecutivas através dele.

Existem duas abordagens para rotação em Go:

Abordagem 1: Lista de proxies com 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 retorna um cliente com o próximo proxy em sequência
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)
    }

    // Cada requisição passa pelo próximo proxy
    for i := 0; i < 10; i++ {
        client := rotator.GetClient()
        resp, err := client.Get("https://httpbin.org/ip")
        if err != nil {
            fmt.Printf("Erro na requisição %d: %v\n", i, err)
            continue
        }
        body, _ := io.ReadAll(resp.Body)
        resp.Body.Close()
        fmt.Printf("Requisição %d: %s\n", i, string(body))
    }
}

Abordagem 2: Seleção dinâmica de proxies através de função

O campo Proxy em http.Transport aceita uma função com a assinatura func(*http.Request) (*url.URL, error). Isso permite escolher proxies dinamicamente para cada requisição:

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) {
        // Seleciona o proxy com base na requisição (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
}

A segunda abordagem é mais elegante — um cliente automaticamente rotaciona proxies para cada requisição. Isso é especialmente conveniente para requisições concorrentes através de goroutines.

Qual tipo de proxy escolher para o projeto Go

A escolha do tipo de proxy afeta diretamente o sucesso do seu projeto. Diferentes tarefas requerem diferentes tipos — vamos analisar as principais opções:

Tipo de proxy Velocidade Anonimato Melhor para
Data Center ⚡ Alta Média Scraping de dados abertos, teste de API, teste de carga
Residencial 🔄 Média Alta Scraping de marketplaces (Wildberries, Ozon), contorno de proteção anti-bot
Móvel 🔄 Média Máxima Trabalho com redes sociais, serviços com proteção anti-bot rigorosa

Para scraping de marketplaces (Wildberries, Ozon, Avito): use proxies residenciais. Essas plataformas utilizam ativamente sistemas de detecção de bots (Cloudflare, soluções próprias) e bloqueiam facilmente IPs de data centers. IPs residenciais parecem usuários domésticos normais.

Para teste de API e teste de carga: proxies de data center são a escolha ideal. Alta velocidade, conexão estável, latência previsível.

Para teste geográfico: proxies residenciais com seleção de país/cidade. Permitem verificar como seu serviço ou anúncio aparece para usuários de uma região específica.

Erros comuns e melhores práticas

Ao longo dos anos trabalhando com proxies em projetos Go, surgiu uma lista de erros típicos que atrasam o desenvolvimento e reduzem a confiabilidade dos parsers. Vamos analisar cada um.

Erro 1: Reutilização do transporte sem reiniciar conexões

http.Transport armazena conexões TCP (keep-alive). Se você cria um novo transporte para cada requisição, perde essa vantagem. Mas se reutilizar um transporte fixo com um proxy ao rotacionar — as requisições podem passar pela conexão antiga, ignorando o novo proxy.

Solução: ao mudar de proxy, chame transport.CloseIdleConnections() ou use uma abordagem funcional com seleção dinâmica de proxies (descrita na seção sobre rotação).

Erro 2: Sem tratamento de erros de proxy

Proxies podem estar indisponíveis, sobrecarregados ou bloqueados pelo site de destino. Sem lógica de retry, seu parser parará na primeira falha.

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) // Atraso exponencial
            continue
        }
        defer resp.Body.Close()

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

        return io.ReadAll(resp.Body)
    }
    return nil, fmt.Errorf("todas as tentativas esgotadas: %w", lastErr)
}

Erro 3: Ignorando a verificação TLS

Alguns desenvolvedores desativam a verificação de certificados TLS para proxies (InsecureSkipVerify: true). Isso cria uma vulnerabilidade para ataques MITM. Se o servidor proxy usa um certificado autoassinado — adicione-o ao pool de confiáveis, em vez de desativar a verificação completamente.

Erro 4: Mesmos cabeçalhos para todas as requisições

Mesmo com a rotação de proxies, você pode ser bloqueado pelo padrão de cabeçalhos. Altere o User-Agent junto com o proxy, adicione cabeçalhos realistas de navegador:

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", "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7")
req.Header.Set("Accept-Encoding", "gzip, deflate, br")

Checklist de um cliente proxy confiável em Go

  • ✅ Timeout no cliente (Timeout: 30 * time.Second)
  • ✅ Lógica de retry com atraso exponencial
  • ✅ Rotação de proxies em erros 403/429
  • ✅ Rotação de User-Agent junto com o proxy
  • ✅ Registro de erros com indicação do proxy utilizado
  • ✅ Credenciais através de variáveis de ambiente, não codificadas
  • ✅ Fechamento de resp.Body através de defer
  • ✅ Limitação de concorrência através de semáforo ou worker pool

Exemplo de um worker pool completo com rotação de proxies

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) // Semáforo para limitar concorrência

    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("Erro: %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
}

Conclusão

Configurar proxies em Go é uma tarefa que pode ser resolvida em algumas linhas de código, mas requer compreensão dos detalhes: uso correto do http.Transport, tratamento adequado de autenticação, rotação inteligente de IP e tratamento de erros. A biblioteca padrão fornece tudo o que é necessário para proxies HTTP, e o pacote golang.org/x/net/proxy atende à necessidade de SOCKS5.

Principais conclusões do artigo:

  • Sempre crie seu próprio http.Clienthttp.DefaultClient não suporta proxies diretamente
  • Use uma abordagem funcional (Proxy: func(r *Request) (*url.URL, error)) para rotação dinâmica
  • Armazene credenciais em variáveis de ambiente, não no código
  • Adicione lógica de retry — proxies não são 100% confiáveis
  • Altere o User-Agent junto com o proxy para máxima eficácia

Se você está escrevendo um parser para marketplaces como Wildberries ou Ozon, recomendamos usar proxies residenciais — eles possuem IPs reais de usuários domésticos e são significativamente menos propensos a serem bloqueados por sistemas anti-bot. Para cenários de alta carga com milhares de requisições por minuto, a escolha ideal são proxies de data center — eles oferecem a máxima velocidade e estabilidade de conexão.

```