Skip to content
Guides 8 Connection Type: 2 views

Using Proxies in Go (Golang)

Explore the essentials of using HTTP and SOCKS proxies in Go (Golang). This guide covers setup, configuration, and practical use cases for secure and anonymous networking.

Using proxies in Go involves configuring an http.Client's http.Transport with a Proxy function, typically http.ProxyURL for static proxies or a custom function for dynamic proxy selection.

Why Use Proxies in Go?

Proxies serve various purposes in network communication, particularly when making HTTP requests from Go applications:

  • Anonymity: Masking the client's original IP address.
  • Geo-targeting: Accessing content or services restricted to specific geographical regions by routing requests through proxies located in those regions.
  • Rate Limiting: Distributing requests across multiple IP addresses to avoid hitting rate limits imposed by target servers.
  • Web Scraping: Facilitating large-scale data collection by rotating IP addresses, bypassing detection, and managing request volume.
  • Security & Filtering: Routing traffic through corporate proxies for security scanning, content filtering, or access control.
  • Debugging: Intercepting and inspecting traffic.

Configuring an HTTP Client with a Proxy

Go's standard library net/http package provides robust support for proxy configurations. The primary mechanism involves modifying the http.Transport associated with an http.Client.

Using http.ProxyFromEnvironment

The simplest way to use a proxy is to leverage environment variables. Go's http.ProxyFromEnvironment function checks for HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables.

  • HTTP_PROXY: Used for http:// requests.
  • HTTPS_PROXY: Used for https:// requests.
  • NO_PROXY: A comma-separated list of hostnames that should bypass the proxy.
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

func main() {
    // Set environment variables (for demonstration)
    os.Setenv("HTTP_PROXY", "http://your_proxy_ip:port")
    os.Setenv("HTTPS_PROXY", "http://your_proxy_ip:port") // Note: HTTPS_PROXY can be an HTTP proxy
    // os.Setenv("NO_PROXY", "localhost,127.0.0.1")

    client := &http.Client{
        Transport: &http.Transport{
            Proxy: http.ProxyFromEnvironment,
        },
        Timeout: 10 * time.Second,
    }

    resp, err := client.Get("http://httpbin.org/ip")
    if err != nil {
        fmt.Printf("Error making request: %v\n", err)
        return
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Error reading response body: %v\n", err)
        return
    }
    fmt.Printf("Response from httpbin.org/ip:\n%s\n", body)

    os.Unsetenv("HTTP_PROXY")
    os.Unsetenv("HTTPS_PROXY")
}

Using http.ProxyURL for Static Proxies

For explicit proxy configuration, http.ProxyURL is used. This function takes a *url.URL representing the proxy address.

package main

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

func main() {
    proxyStr := "http://your_proxy_ip:port" // Replace with your proxy address
    proxyURL, err := url.Parse(proxyStr)
    if err != nil {
        fmt.Printf("Error parsing proxy URL: %v\n", err)
        return
    }

    client := &http.Client{
        Transport: &http.Transport{
            Proxy: http.ProxyURL(proxyURL),
        },
        Timeout: 10 * time.Second,
    }

    resp, err := client.Get("http://httpbin.org/ip")
    if err != nil {
        fmt.Printf("Error making request: %v\n", err)
        return
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Error reading response body: %v\n", err)
        return
    }
    fmt.Printf("Response from httpbin.org/ip:\n%s\n", body)
}

Proxy Authentication

Many proxies require authentication. For HTTP/HTTPS proxies using Basic Authentication, credentials can be embedded directly into the proxy URL:

package main

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

func main() {
    // Format: "http://username:password@proxy_ip:port"
    proxyStr := "http://user:password@your_auth_proxy_ip:port" // Replace with your authenticated proxy
    proxyURL, err := url.Parse(proxyStr)
    if err != nil {
        fmt.Printf("Error parsing proxy URL: %v\n", err)
        return
    }

    client := &http.Client{
        Transport: &http.Transport{
            Proxy: http.ProxyURL(proxyURL),
        },
        Timeout: 10 * time.Second,
    }

    resp, err := client.Get("http://httpbin.org/ip")
    if err != nil {
        fmt.Printf("Error making request with authenticated proxy: %v\n", err)
        return
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Error reading response body: %v\n", err)
        return
    }
    fmt.Printf("Response from httpbin.org/ip via authenticated proxy:\n%s\n", body)
}

Using SOCKS5 Proxies

While net/http directly supports HTTP/HTTPS proxies, SOCKS5 proxies require an additional package: golang.org/x/net/proxy. SOCKS5 operates at a lower level than HTTP proxies, supporting various protocols.

To use a SOCKS5 proxy, you need to create a Dialer using the proxy package and then assign it to the DialContext field of http.Transport.

package main

import (
    "context"
    "fmt"
    "io"
    "net"
    "net/http"
    "time"

    "golang.org/x/net/proxy" // Make sure to 'go get golang.org/x/net/proxy'
)

func main() {
    socks5Proxy := "socks5://user:password@your_socks5_proxy_ip:port" // Replace with your SOCKS5 proxy

    dialer, err := proxy.SOCKS5("tcp", socks5Proxy[len("socks5://"):], nil, proxy.Direct) // Basic auth is handled by the dialer
    if err != nil {
        fmt.Printf("Error creating SOCKS5 dialer: %v\n", err)
        return
    }

    // For authenticated SOCKS5, the proxy.SOCKS5 function handles it if the URL contains credentials.
    // If the proxy.SOCKS5 function doesn't parse credentials directly, you might need to
    // extract them and pass a proxy.Auth struct. Example:
    // auth := &proxy.Auth{User: "user", Password: "password"}
    // dialer, err := proxy.SOCKS5("tcp", "your_socks5_proxy_ip:port", auth, proxy.Direct)

    client := &http.Client{
        Transport: &http.Transport{
            DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
                return dialer.Dial(network, addr)
            },
            // Optionally, set TLSClientConfig for HTTPS over SOCKS5
            // TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // Use with caution
        },
        Timeout: 10 * time.Second,
    }

    resp, err := client.Get("http://httpbin.org/ip")
    if err != nil {
        fmt.Printf("Error making request with SOCKS5 proxy: %v\n", err)
        return
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Error reading response body: %v\n", err)
        return
    }
    fmt.Printf("Response from httpbin.org/ip via SOCKS5 proxy:\n%s\n", body)
}

Dynamic Proxy Selection and Rotation

For applications like web scraping or large-scale data gathering, proxy rotation is essential to avoid IP blocks and manage request volume. This requires a custom Proxy function for http.Transport.

The Proxy field of http.Transport expects a function with the signature func(*http.Request) (*url.URL, error). This function is called before each request, allowing dynamic proxy selection based on request properties or a rotation strategy.

package main

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

// ProxyRotator manages a list of proxies and provides a round-robin selection.
type ProxyRotator struct {
    proxies []*url.URL
    current int
    mu      sync.Mutex
}

// NewProxyRotator creates a new ProxyRotator from a slice of proxy URLs.
func NewProxyRotator(proxyStrings []string) (*ProxyRotator, error) {
    proxies := make([]*url.URL, len(proxyStrings))
    for i, s := range proxyStrings {
        u, err := url.Parse(s)
        if err != nil {
            return nil, fmt.Errorf("invalid proxy URL '%s': %w", s, err)
        }
        proxies[i] = u
    }
    return &ProxyRotator{
        proxies: proxies,
        current: 0,
    }, nil
}

// GetProxy returns the next proxy in a round-robin fashion.
func (pr *ProxyRotator) GetProxy(_ *http.Request) (*url.URL, error) {
    if len(pr.proxies) == 0 {
        return nil, nil // No proxy
    }

    pr.mu.Lock()
    defer pr.mu.Unlock()

    proxy := pr.proxies[pr.current]
    pr.current = (pr.current + 1) % len(pr.proxies)
    return proxy, nil
}

func main() {
    proxyList := []string{
        "http://user1:pass1@proxy1.example.com:8080",
        "http://user2:pass2@proxy2.example.com:8080",
        "http://user3:pass3@proxy3.example.com:8080",
    }

    rotator, err := NewProxyRotator(proxyList)
    if err != nil {
        fmt.Printf("Error creating proxy rotator: %v\n", err)
        return
    }

    client := &http.Client{
        Transport: &http.Transport{
            Proxy: rotator.GetProxy, // Assign the rotator's GetProxy method
        },
        Timeout: 15 * time.Second,
    }

    for i := 0; i < 5; i++ {
        resp, err := client.Get("http://httpbin.org/ip")
        if err != nil {
            fmt.Printf("Request %d error: %v\n", i+1, err)
            continue
        }
        defer resp.Body.Close()

        body, err := io.ReadAll(resp.Body)
        if err != nil {
            fmt.Printf("Request %d error reading body: %v\n", i+1, err)
            continue
        }
        fmt.Printf("Request %d IP: %s\n", i+1, body)
        time.Sleep(1 * time.Second) // Simulate some work
    }
}

This example implements a basic round-robin rotator. More advanced rotators might:
* Track proxy health and remove failing proxies.
* Implement different rotation strategies (e.g., weighted, random).
* Integrate with external proxy management services.

HTTPS Traffic Through Proxies

When an http.Client makes an https:// request through an HTTP proxy, the http.Transport performs a CONNECT method handshake with the proxy. The client asks the proxy to establish a TCP tunnel to the target host and port (typically 443). Once the tunnel is established, the client performs the TLS handshake directly with the target server through the proxy tunnel. The proxy itself does not decrypt the HTTPS traffic (unless it's a "man-in-the-middle" proxy, which is a different scenario requiring specific trust configurations).

For https:// requests, the Proxy function should return the proxy URL as usual. Go's net/http handles the CONNECT method automatically.

TLS/SSL Verification

By default, Go clients perform strict TLS certificate verification. If the target server's certificate cannot be verified (e.g., self-signed, expired, or untrusted CA), the request will fail.

In specific scenarios (e.g., testing with known self-signed certificates in a controlled environment), you might disable certificate verification using InsecureSkipVerify in tls.Config.

package main

import (
    "crypto/tls"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "time"
)

func main() {
    proxyStr := "http://your_proxy_ip:port"
    proxyURL, err := url.Parse(proxyStr)
    if err != nil {
        fmt.Printf("Error parsing proxy URL: %v\n", err)
        return
    }

    // Configure a custom Transport for TLS settings
    tr := &http.Transport{
        Proxy: http.ProxyURL(proxyURL),
        TLSClientConfig: &tls.Config{
            // WARNING: InsecureSkipVerify should not be used in production environments.
            // It disables certificate chain and hostname verification.
            InsecureSkipVerify: true,
        },
        DialContext: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).DialContext,
        MaxIdleConns:        100,
        IdleConnTimeout:     90 * time.Second,
        TLSHandshakeTimeout: 10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }

    client := &http.Client{
        Transport: tr,
        Timeout: 15 * time.Second, // Overall request timeout
    }

    // Example target for HTTPS
    resp, err := client.Get("https://example.com")
    if err != nil {
        fmt.Printf("Error making HTTPS request with proxy: %v\n", err)
        return
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Error reading response body: %v\n", err)
        return
    }
    fmt.Printf("Response status from https://example.com: %s\n", resp.Status)
    // fmt.Printf("Response body: %s\n", body) // Omitted for brevity
    _ = body
}

Comparison: HTTP/HTTPS vs. SOCKS5 Proxies

Feature HTTP/HTTPS Proxy SOCKS5 Proxy
Protocol Layer Application Layer (Layer 7) Session Layer (Layer 5)
Supported Traffic Primarily HTTP/HTTPS requests Any TCP/UDP traffic (HTTP, FTP, SMTP, DNS, etc.)
Traffic Filtering Can inspect HTTP headers, URLs, and content Generally does not inspect traffic content
Authentication HTTP Basic, Digest (Go only supports Basic in URL) Username/Password (SOCKS5 AUTH method)
Ease of Use (Go) Native support in net/http (http.ProxyURL) Requires golang.org/x/net/proxy package
Performance Potentially higher overhead due to parsing HTTP Generally lower overhead, acts as a simple relay
Use Cases Web scraping, API interaction, web filtering General network tunneling, bypassing firewalls

Best Practices

  • Set Timeouts: Always configure timeouts for http.Client and http.Transport (e.g., DialContext, TLSHandshakeTimeout) to prevent requests from hanging indefinitely, especially when dealing with unreliable proxies.
  • Robust Error Handling: Proxy-related errors can vary (e.g., net.OpError for connection issues, io.EOF for unexpected disconnections). Implement comprehensive error handling and retry logic.
  • Proxy Management: For dynamic scenarios, implement a robust proxy management system that monitors proxy health, rotates proxies effectively, and handles failures gracefully.
  • Resource Management: Ensure resp.Body.Close() is called using defer to release network resources.
  • Context for Cancellation: Use context.Context with http.Request to enable request cancellation and timeout management across functions.
  • Avoid InsecureSkipVerify: Do not use InsecureSkipVerify = true in production environments unless you fully understand the security implications and have compensating controls.
Auto-update: 03.03.2026
All Categories

Advantages of our proxies

25,000+ proxies from 120+ countries