The essential points from this guide -- each one is explained in detail below.
Set http.Transport.Proxy to http.ProxyURL(parsedURL) for explicit proxy routing in Go.
Go's http.ProxyFromEnvironment reads HTTP_PROXY and HTTPS_PROXY environment variables by default.
For SOCKS5, use golang.org/x/net/proxy.SOCKS5() to create a dialer and wrap it in a custom transport.
Custom transports let you control TLS settings, timeouts, and connection pooling alongside proxy config.
Go's standard library provides full proxy support through the http.Transport struct. Set the Proxy field to a function that returns the proxy URL for each request. The simplest approach uses http.ProxyURL, which routes all requests through a single proxy endpoint.
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"time"
)
func main() {
proxyURL, _ := url.Parse("http://USER:PASS@gw.knoxproxy.com:7000")
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
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))
}The transport is safe for concurrent use, so you can share a single http.Client across goroutines. Each request through the backconnect gateway gets a different exit IP, making this setup suitable for concurrent scraping with goroutines.
Go's default HTTP transport uses http.ProxyFromEnvironment, which reads the HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables. This means the standard http.DefaultClient works with proxies out of the box when these variables are set -- no code changes required.
export HTTP_PROXY="http://USER:PASS@gw.knoxproxy.com:7000"
export HTTPS_PROXY="http://USER:PASS@gw.knoxproxy.com:7000"
export NO_PROXY="localhost,127.0.0.1"// With environment variables set, the default client proxies automatically
resp, err := http.Get("https://httpbin.org/ip")This approach is ideal for deployed services where the proxy configuration should be external to the code. Note that Go also supports lowercase variants (http_proxy, https_proxy) but the uppercase versions take precedence when both are set.
For SOCKS5 proxy support, use the golang.org/x/net/proxy package. It provides a SOCKS5 dialer that you can plug into an http.Transport as a custom DialContext function. This is necessary because Go's built-in proxy support only handles HTTP CONNECT proxies natively.
import (
"context"
"net"
"net/http"
"golang.org/x/net/proxy"
)
func main() {
auth := &proxy.Auth{User: "USER", Password: "PASS"}
dialer, err := proxy.SOCKS5("tcp", "gw.knoxproxy.com:7001", auth, proxy.Direct)
if err != nil {
panic(err)
}
transport := &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer.Dial(network, addr)
},
}
client := &http.Client{Transport: transport}
resp, _ := client.Get("https://httpbin.org/ip")
defer resp.Body.Close()
}The SOCKS5 dialer routes all TCP connections through the proxy, including the TLS handshake. This provides better anonymity than HTTP CONNECT because the proxy sees only the destination IP and port, not the HTTP headers or URL path.
Go's goroutines make concurrent proxied scraping straightforward. Create a single http.Client with a proxied transport and share it across goroutines. The backconnect gateway assigns a different exit IP per connection, so concurrent requests naturally get different IPs without explicit rotation logic.
func scrape(client *http.Client, urls []string) {
var wg sync.WaitGroup
sem := make(chan struct{}, 20) // Limit to 20 concurrent requests
for _, u := range urls {
wg.Add(1)
go func(target string) {
defer wg.Done()
sem <- struct{}{} // Acquire semaphore
defer func() { <-sem }() // Release semaphore
resp, err := client.Get(target)
if err != nil {
log.Printf("Error fetching %s: %v", target, err)
return
}
defer resp.Body.Close()
// Process response
}(u)
}
wg.Wait()
}Use a semaphore channel to limit concurrency and avoid overwhelming the target site or exhausting file descriptors. A pool of 10-50 concurrent connections is a reasonable starting point. The proxied transport handles connection reuse and TLS session caching across goroutines.
Ready to put this into practice? Browse Residential Proxies
KnoxProxy Research Team · Technical Content
Network engineers and proxy infrastructure specialists with 10+ years in anti-bot systems, web scraping, and IP routing.
90.4M+ ethically sourced residential IPs across 195 countries. Start free -- no credit card required.