How to use proxies in Go
In Go, set a proxy on an http.Transport: parse http://USERNAME:PASSWORD@HOST:PORT with url.Parse and pass it as Proxy: http.ProxyURL(proxyURL). Go sends the credentials as Proxy-Authorization automatically, including for HTTPS sites.
What you need
- An active CleanProxies plan: static ISP proxies for fixed US IPs, or rotating residential / mobile proxies for pools. The trial works too.
- Your credential line from the dashboard's proxy manager: host (or IP), port, username and password.
- Go 1.21 or later (any recent release works).
- Only the standard library;
golang.org/x/net/proxyis optional, for non-HTTP traffic over SOCKS5.
New to proxies? For the overview, read Using proxies with cURL, Python and Node.js and SOCKS5 vs HTTP proxies. This guide goes deeper on Go only.
Your CleanProxies credentials
| Product | Line in your dashboard | Notes |
|---|---|---|
| Static ISP proxies | IP:PORT:USER:PASS | One line per dedicated IP. With IP whitelisting turned on, the line is just IP:PORT and no password is sent. |
| Rotating residential and mobile | HOST:PORT:USER:PASS | Generated in the proxy manager. The username carries the location and session you pick, so generate a new list to change them. |
Most tools want the same details as a URL: http://USERNAME:PASSWORD@HOST:PORT for HTTP(S) or socks5://USERNAME:PASSWORD@HOST:PORT for SOCKS5. The examples on this page use those placeholders; copy the host, port, username and password from your CleanProxies dashboard. To convert a whole list at once, paste it into the proxy list formatter.
How to set up CleanProxies in Go
- 1
Build the proxy URL
Turn your dashboard line intohttp://USERNAME:PASSWORD@HOST:PORT. Useurl.UserPasswordif the password has special characters, so it is escaped for you. - 2
Create one Transport per proxy
SetProxy: http.ProxyURL(u)on anhttp.Transportand wrap it in anhttp.Clientwith a timeout. - 3
Reuse the client
A client and its transport are safe for concurrent use and pool connections, so create them once per proxy, not per request. - 4
Check the exit IP
Requesthttps://api.ipify.organd compare the result with the IP in your dashboard.
HTTP(S) proxy with net/http
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"time"
)
func main() {
proxyURL := &url.URL{
Scheme: "http",
User: url.UserPassword("USERNAME", "PASSWORD"),
Host: "HOST:PORT",
}
client := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}
resp, err := client.Get("https://api.ipify.org")
if err != nil {
panic(err)
}
defer resp.Body.Close()
ip, _ := io.ReadAll(resp.Body)
fmt.Println("exit IP:", string(ip))
}http.DefaultTransport already uses http.ProxyFromEnvironment, so a plain http.Get honours HTTPS_PROXY, HTTP_PROXY and NO_PROXY. Setting the proxy in code is clearer once you run more than one.
SOCKS5 proxy in Go
http.Transport understands socks5:// proxy URLs natively: the user info becomes the SOCKS5 username and password, and the hostname is sent to the proxy, so DNS is resolved on the proxy side.
proxyURL := &url.URL{
Scheme: "socks5",
User: url.UserPassword("USERNAME", "PASSWORD"),
Host: "HOST:PORT",
}
client := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
}For raw TCP connections that aren't HTTP, use proxy.SOCKS5("tcp", "HOST:PORT", &proxy.Auth{User: "USERNAME", Password: "PASSWORD"}, proxy.Direct) from golang.org/x/net/proxy and dial through it.
Spread goroutines across a list of ISP proxies
Give each proxy its own client and hand work out round-robin. Each ISP line is a 100% dedicated IP, so this pattern keeps the load per IP predictable.
func loadClients(path string) ([]*http.Client, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var clients []*http.Client
for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
parts := strings.SplitN(strings.TrimSpace(line), ":", 4) // IP:PORT:USER:PASS
if len(parts) != 4 {
continue
}
u := &url.URL{Scheme: "http", User: url.UserPassword(parts[2], parts[3]), Host: parts[0] + ":" + parts[1]}
clients = append(clients, &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{Proxy: http.ProxyURL(u), MaxIdleConnsPerHost: 10},
})
}
return clients, nil
}
// in your worker loop: client := clients[i%len(clients)]A kept-alive connection stays on the exit IP it opened with. With rotating residential proxies, set DisableKeepAlives: true on the transport when you want every request to get a fresh IP.
Static ISP vs rotating residential in Go
Static ISP proxies
Each transport pins one dedicated US ISP IP for as long as you hold it, which suits logged-in sessions, API polling and monitoring that should come from one address.
Rotating residential proxies
Point one client at the rotating gateway line and new connections pick up new IPs. Generate a sticky list in the dashboard when a multi-step flow must stay on one IP, and keep keep-alives on for it.
Verify your exit IP
Run the HTTP example above: it prints the exit IP. Run it twice against a rotating residential line with keep-alives disabled and you should see two different addresses.
Open What is my IP? through the proxy to see the address and headers a website receives, or paste up to ten lines into the free proxy checker to test connectivity, latency and exit IP before you start.
Troubleshooting Go proxy errors
| Problem | Likely cause | Fix |
|---|---|---|
| Proxy Authentication Required (407) | Credentials missing from the URL, or a password with @/: that broke parsing. | Build the URL with url.UserPassword instead of string concatenation. |
| proxyconnect tcp: dial tcp: i/o timeout | Wrong host or port, or outbound traffic blocked. | Test the same line with curl -x; check the port and your firewall. |
| x509: certificate signed by unknown authority | A corporate TLS inspector, or a proxy URL with the https scheme. | Use the http scheme for the proxy. Never set InsecureSkipVerify to work around it. |
| Same IP on every request with a rotating list | Connection reuse through keep-alive. | Set DisableKeepAlives: true or close idle connections between requests. |
| DNS lookups leave from your server | A custom DialContext that resolves hostnames before the proxy sees them. | Use the socks5:// proxy URL on the transport, which sends hostnames to the proxy. |
Still stuck? Our 24/7 support team answers fast: contact us with the error and the line you used (leave the password out).
Go proxy FAQs
Yes. When the proxy URL has user info, the transport sends a Proxy-Authorization header on the CONNECT request it uses for HTTPS targets, and on plain HTTP requests.