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:
url.Parse— analisa a string com o endereço do proxy em uma estrutura*url.URLhttp.Transport{Proxy: http.ProxyURL(proxyURL)}— cria um transporte que direciona todas as requisições através do proxy especificadohttp.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.Bodyatravés dedefer - ✅ 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.Client—http.DefaultClientnã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.
```