-
Create a module
Every example on this page is a single
main.gofile that uses the standard library, so a fresh module is all you need. Run each example withgo run ..Shell mkdir proxydemo && cd proxydemo go mod init proxydemo -
Send a request through the gateway
Give the transport a proxy URL with your credentials. Go sends them as a Proxy-Authorization header, including on the CONNECT request that tunnels HTTPS.
Go package main import ( "fmt" "io" "log" "net/http" "net/url" "time" ) func main() { proxyURL, err := url.Parse("http://USERNAME:[email protected]:8000") if err != nil { log.Fatal(err) } client := &http.Client{ Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}, Timeout: 60 * time.Second, } resp, err := client.Get("https://api.ipify.org") if err != nil { log.Fatal(err) } defer resp.Body.Close() ip, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Println("Exit IP:", string(ip)) } -
Rotate or keep the same IP
The gateway assigns an IP per connection, and http.Transport keeps connections alive. Disable keep-alives to get a new IP on every request, or keep them on and add a
-session-ID to hold one IP for up to 120 minutes.Go package main import ( "crypto/rand" "encoding/hex" "fmt" "io" "net/http" "net/url" "strings" "time" ) // proxonymClient adds parameters such as "country-us" or "session-a7k2" to the username. func proxonymClient(keepAlive bool, params ...string) *http.Client { user := strings.Join(append([]string{"USERNAME"}, params...), "-") proxyURL := &url.URL{ Scheme: "http", User: url.UserPassword(user, "PASSWORD"), Host: "gw.proxonym.com:8000", } return &http.Client{ Transport: &http.Transport{ Proxy: http.ProxyURL(proxyURL), DisableKeepAlives: !keepAlive, }, Timeout: 60 * time.Second, } } func newSessionID() string { b := make([]byte, 4) rand.Read(b) return hex.EncodeToString(b) } func exitIP(client *http.Client) string { resp, err := client.Get("https://api.ipify.org") if err != nil { return "error: " + err.Error() } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) return string(body) } func main() { // Rotating: keep-alives off, so each request opens a new connection and gets a new IP rotating := proxonymClient(false, "country-us") for i := 0; i < 3; i++ { fmt.Println("rotating", exitIP(rotating)) } // Sticky: one session ID keeps the same IP for up to 30 minutes sticky := proxonymClient(true, "country-us", "session-"+newSessionID(), "lifetime-30") for i := 0; i < 3; i++ { fmt.Println("sticky", exitIP(sticky)) } } -
Target a country, state, city or ASN
Pass location parameters to
proxonymClient()from the previous step. Use lowercase ISO country codes and underscores for spaces, and choose a state or a city, not both.Go clients := map[string]*http.Client{ "California": proxonymClient(true, "country-us", "state-california"), "London": proxonymClient(true, "country-gb", "city-london"), "Comcast": proxonymClient(true, "country-us", "asn-7922"), } for name, client := range clients { resp, err := client.Get("https://ipinfo.io/json") if err != nil { fmt.Println(name, "error:", err) continue } body, _ := io.ReadAll(resp.Body) resp.Body.Close() fmt.Println(name, string(body)) } -
Use the SOCKS5 endpoint
net/http also accepts
socks5://proxy URLs with a username and password. Hostnames are sent to the proxy unresolved, so DNS lookups happen on the gateway side.Go proxyURL, err := url.Parse("socks5://USERNAME-country-gb:[email protected]:1080") if err != nil { log.Fatal(err) } client := &http.Client{ Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}, Timeout: 60 * time.Second, } resp, err := client.Get("https://api.ipify.org") if err != nil { log.Fatal(err) } defer resp.Body.Close() ip, _ := io.ReadAll(resp.Body) fmt.Println("Exit IP:", string(ip)) -
Read gateway status codes and retry
The
OnProxyConnectResponsehook exposes the status of every CONNECT request. Retry 429, 502 and 504 with a backoff, and stop on 402, 403 or 407, which point to balance, policy or credential problems.Go package main import ( "context" "errors" "fmt" "net/http" "net/url" "time" ) // gatewayError carries the status the gateway returned for a CONNECT request. type gatewayError struct{ status int } func (e *gatewayError) Error() string { return fmt.Sprintf("gateway returned %d", e.status) } func newClient(proxyURL *url.URL) *http.Client { return &http.Client{ Timeout: 60 * time.Second, Transport: &http.Transport{ Proxy: http.ProxyURL(proxyURL), OnProxyConnectResponse: func(_ context.Context, _ *url.URL, _ *http.Request, res *http.Response) error { if res.StatusCode != http.StatusOK { return &gatewayError{status: res.StatusCode} } return nil }, }, } } func getWithRetry(client *http.Client, target string, tries int) (*http.Response, error) { var err error for attempt := 1; attempt <= tries; attempt++ { var resp *http.Response resp, err = client.Get(target) if err == nil && resp.StatusCode != http.StatusTooManyRequests && resp.StatusCode < 500 { return resp, nil } if err == nil { resp.Body.Close() err = fmt.Errorf("target returned %d", resp.StatusCode) } var gwErr *gatewayError if errors.As(err, &gwErr) && (gwErr.status == 402 || gwErr.status == 403 || gwErr.status == 407) { return nil, err // retrying will not help: fix the balance, target or credentials } time.Sleep(time.Duration(attempt) * time.Second) } return nil, err } func main() { proxyURL, _ := url.Parse("http://USERNAME-country-us:[email protected]:8000") resp, err := getWithRetry(newClient(proxyURL), "https://example.com", 3) if err != nil { fmt.Println("request failed:", err) return } defer resp.Body.Close() fmt.Println("status:", resp.StatusCode) }
Frequently asked questions
Does Go support SOCKS5 proxies without extra packages?
Yes. net/http accepts socks5:// proxy URLs, including a username and password, and sends hostnames to the proxy for resolution. For raw TCP connections outside HTTP, the golang.org/x/net/proxy package provides a SOCKS5 dialer that takes the same credentials and username parameters as the HTTP endpoint.
Why do all requests from one client show the same IP?
The gateway picks an IP per connection, and http.Transport reuses connections to the same host while they stay open. Set DisableKeepAlives to true on a rotating client so each request opens a new connection, or create separate clients with different session IDs when you need several stable IPs at once.
How do I see the gateway status for HTTPS requests?
For HTTPS targets the gateway status belongs to the CONNECT request, which normally surfaces only as an error string. Since Go 1.20 the OnProxyConnectResponse hook on http.Transport receives the CONNECT response, so you can return a typed error that carries the status code and decide whether to retry.
How many goroutines can share the gateway?
Proxonym places no limit on concurrent sessions, and each account has a burst limit of around 10,000 requests per second. In practice the target site sets the pace, so cap concurrency with a worker pool or semaphore, tune MaxConnsPerHost on the transport, and back off when you see 429 responses.
