Golang HTTP Proxy: Go net/http Transport and SOCKS5 Setup
Route Go net/http through an authenticated proxy with Transport.Proxy, ProxyFromEnvironment or a SOCKS5 dialer, verify the observed exit, and diagnose CONNECT 407, deadline and DNS errors by hop.
gw.databay.co:8888Operating principle
Give each Databay username one reused Transport, let Transport.Proxy or a SOCKS5 dialer own the proxy hop, and observe the exit before real traffic.
How Go net/http Decides Whether a Request Uses a Proxy
In Go, proxy routing belongs to the http.Transport. For each request it calls its Proxy function, and the documentation is explicit: if Proxy is nil or returns a nil *url.URL, no proxy is used. http.DefaultTransport sets Proxy: http.ProxyFromEnvironment, but a Transport you construct starts with a nil Proxy and silently ignores the environment. A custom Proxy function should return an error, which aborts the request, rather than nil when it cannot choose a route.
Through an http proxy, http:// destinations are forwarded in absolute form, while https:// destinations get a CONNECT tunnel with destination TLS negotiated end to end inside it. Databay's gateway, gw.databay.co:8888, serves HTTP proxying, HTTPS CONNECT and SOCKS5, with no UDP; Go's https proxy scheme, which means TLS to the proxy itself, is not one of those modes. Route only traffic to destinations you own or are permitted to access, and follow each destination's terms.
Map Databay Gateway Values to Go Transport Fields
Each row pairs a Databay value with a documented control and the evidence to check.
| Go net/http control | Databay value | What to verify |
|---|---|---|
Transport.Proxy set to http.ProxyURL(u) | u.Scheme http, u.Host gw.databay.co:8888 | Same URL for every request; NO_PROXY is never consulted |
u.User from url.UserPassword | Dashboard username and password via DATABAY_PROXY_USER and DATABAY_PROXY_PASS | Both loaded at start-up; neither printed |
Transport.ProxyConnectHeader | Optional headers for CONNECT only | Not sent for http:// destinations |
Transport.OnProxyConnectResponse | A logging hook, no secret | CONNECT status recorded before a non-200 becomes an error |
http.ProxyFromEnvironment | HTTPS_PROXY, HTTP_PROXY, NO_PROXY | Scheme-matched variable set; no exclusion matches |
proxy.SOCKS5 in Transport.DialContext | "tcp", gw.databay.co:8888, proxy.Auth | Transport.Proxy is nil, so no HTTP hop is stacked |
Routing lives in the username. The documented residential example is USER-zone-residential-countryCode-us, and USER-zone-residential-countryCode-us-sessionId-abc123 adds a sticky session. Copy the complete username the dashboard shows rather than inventing segments for other pools. Premium Residential accepts one of country, state, city, ZIP, coordinate or ASN targeting at a time; Residential Flex, Datacenter and Mobile target a country or continent. All pools are shared.
A Complete Go Program With Environment Credentials
The program clones http.DefaultTransport to keep its documented dial, TLS handshake and HTTP/2 defaults, then changes only the proxy decision and pool settings. Credentials come from variables your secret manager injects.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"time"
)
func newGatewayClient(user, pass string) *http.Client {
proxyURL := &url.URL{
Scheme: "http",
Host: "gw.databay.co:8888",
User: url.UserPassword(user, pass),
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.Proxy = http.ProxyURL(proxyURL)
transport.MaxIdleConns = 20
transport.IdleConnTimeout = 60 * time.Second
return &http.Client{Transport: transport, Timeout: 30 * time.Second}
}
func run() error {
user, pass := os.Getenv("DATABAY_PROXY_USER"), os.Getenv("DATABAY_PROXY_PASS")
if user == "" || pass == "" {
return errors.New("proxy credentials are not set")
}
client := newGatewayClient(user, pass)
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet,
"https://api.ipify.org?format=json", nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("via gw.databay.co:8888: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("destination answered %s", resp.Status)
}
var observed struct {
IP string `json:"ip"`
}
if err := json.NewDecoder(io.LimitReader(resp.Body, 4096)).Decode(&observed); err != nil {
return err
}
fmt.Println("observed exit:", observed.IP)
return nil
}
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}url.UserPassword stores raw values, so a password containing @ or : needs no hand-encoding, and the Transport derives Proxy-Authorization from that userinfo. Errors from client.Do name the destination, so the program adds the gateway host instead of formatting proxyURL, whose String method includes the password. The 4,096-byte read cap matters because Databay bills traffic per GB.
Ready to run this for real?
Credentials appear in your dashboard in minutes, on the same gateway and username format this Go net/http guide uses.
ProxyFromEnvironment, HTTPS_PROXY and NO_PROXY Rules
http.ProxyFromEnvironment reads HTTP_PROXY, HTTPS_PROXY and NO_PROXY, or their lowercase versions, and uses the variable matching the destination scheme: https:// requests consult only HTTPS_PROXY, so setting HTTP_PROXY alone leaves them direct. A value may be a full URL or host[:port], which assumes http. Requests to localhost and, per the httpproxy documentation, loopback addresses never use it, so a local test server proves nothing about the gateway.
NO_PROXY takes comma-separated IP addresses, CIDR prefixes, domain names that also match their subdomains, leading-dot names that match subdomains only, optional ports, or a single * that disables proxying. A stale inherited entry makes a correct client look broken. Current releases read the environment once per process, so set it before start-up. Authenticating this way puts the secret inside the value, as in HTTPS_PROXY=http://USER:PASS@gw.databay.co:8888, shown here only so you can recognize it, not as a recommendation: child processes inherit it and environment dumps capture it.
Carry Proxy Credentials in Go Without Leaking Them
Userinfo on the proxy URL covers both request shapes: absolute-form requests for http:// destinations and CONNECT for https://. Transport.ProxyConnectHeader applies to CONNECT only; GetProxyConnectHeader, its dynamic form, overrides it when set. Carrying the credential there keeps the URL free of secrets, but http:// destinations on that Transport then go unauthenticated, so use one carrier per Transport.
// HTTPS destinations only: the proxy URL carries no userinfo.
token := base64.StdEncoding.EncodeToString([]byte(user + ":" + pass))
transport.Proxy = http.ProxyURL(&url.URL{Scheme: "http", Host: "gw.databay.co:8888"})
transport.ProxyConnectHeader = http.Header{"Proxy-Authorization": {"Basic " + token}}
// Log the CONNECT status before a non-200 becomes an error.
transport.OnProxyConnectResponse = func(ctx context.Context, proxyURL *url.URL,
connectReq *http.Request, connectRes *http.Response) error {
log.Printf("CONNECT %s via %s: %d", connectReq.Host, proxyURL.Host, connectRes.StatusCode)
return nil
}Keep both values in a secret manager, inject them at runtime, and never commit, screenshot or log them. Do not print the Transport, the proxy URL or the header map. URL.Redacted masks only the password, leaving the routing username visible.
SOCKS5 With golang.org/x/net/proxy or the socks5 Scheme
Transport.Proxy accepts socks5 URLs and treats socks5 the same as socks5h, so http.ProxyURL with Scheme: "socks5" and the same userinfo works without extra modules. For an explicit dialer, golang.org/x/net/proxy provides SOCKS5, which returns a Dialer for RFC 1928 connections with optional username and password authentication. This variant replaces newGatewayClient; run go get golang.org/x/net and add the net and proxy imports:
func newSOCKS5Client(user, pass string) (*http.Client, error) {
forward := &net.Dialer{Timeout: 10 * time.Second, KeepAlive: 30 * time.Second}
dialer, err := proxy.SOCKS5("tcp", "gw.databay.co:8888",
&proxy.Auth{User: user, Password: pass}, forward)
if err != nil {
return nil, err
}
contextDialer, ok := dialer.(proxy.ContextDialer)
if !ok {
return nil, errors.New("SOCKS5 dialer lacks DialContext")
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.Proxy = nil // SOCKS5 is the only proxy hop
transport.DialContext = contextDialer.DialContext
return &http.Client{Transport: transport, Timeout: 30 * time.Second}, nil
}Setting Proxy to nil matters: the clone inherits ProxyFromEnvironment, and an inherited HTTPS_PROXY would be dialed through the SOCKS tunnel as a stacked hop. The ContextDialer assertion supplies DialContext, so request deadlines also bound the SOCKS handshake. On either path, destination host names go to the gateway for resolution, and only TCP is carried. The HTTP versus SOCKS5 comparison covers the trade-offs.
Reuse One Go Client per Route and Bound Every Request
The net/http documentation says Clients and Transports are safe for concurrent use and should be created once and reused. Build one Transport per proxy configuration, meaning one per Databay username, and share it across goroutines. MaxIdleConns caps idle connections across hosts, MaxIdleConnsPerHost falls back to 2 when unset, and IdleConnTimeout closes idle connections after the interval. Read bodies to EOF and close them so connections can be reused.
Reuse affects routing: a new proxy connection may select an available exit, while a reused connection or tunnel, including one HTTP/2 connection multiplexing many requests, may retain the same exit. Neither outcome is promised, and DisableKeepAlives forces new connections, not new IPs. Client.Timeout covers connection time, redirects and the body read; a context from http.NewRequestWithContext controls the entire lifetime of one request. Use the context per call and Client.Timeout as a backstop. Retry only idempotent requests, and never retry or switch sessions on 401, 403, 407 or 429.
Verify the Observed Exit Before the Real Destination
- Baseline: request
https://api.ipify.org?format=jsononce with a nilProxyand note the address. - Gateway: run the program. A result equal to the baseline means the request went direct: check for a nil
Proxy, aNO_PROXYmatch or aProxyfunction returning nil. - Second observer: request
https://databay.com/what-is-my-ip/jsonthrough the same client. A rotating username may show another exit there, because a new tunnel may select one. - Continuity: for a sessionId username, repeat at checkpoints inside the session window (up to 1440 minutes on Premium Residential and Residential Flex where available, or up to 120 minutes on Datacenter and Mobile). Matches show continuity for those observations only because the IP can change before the window ends.
- Destination: only then call the authorized destination, with the same client.
Log the Go version, UTC time, route class and observed IP, never the password. An observed exit is point-in-time evidence, not proof of anonymity. The What Is My IP page gives a second observation outside your code.
Troubleshoot Go Proxy Errors by Hop: 407, Deadlines, DNS
Proxy-hop failures (gateway DNS, TCP, authentication, CONNECT or SOCKS negotiation) occur before any destination byte moves. Destination-hop failures (TLS inside the tunnel, then the HTTP status) mean the gateway did its part.
| Evidence | Hop | Next safe check |
|---|---|---|
proxyconnect tcp: dial tcp: lookup gw.databay.co ending in no such host | Proxy hop: gateway DNS | Through an HTTP proxy only the gateway name resolves locally. On Unix, GODEBUG=netdns=go+1 or cgo+1 prints the resolver decision. |
proxyconnect tcp with i/o timeout or a refused connection | Proxy hop: TCP | Egress rules for port 8888 |
HTTPS destination: Do returns an error with a reason phrase such as Proxy Authentication Required | Proxy hop: CONNECT authentication | Status from OnProxyConnectResponse, loaded variables, complete username |
HTTP destination: resp.StatusCode equals http.StatusProxyAuthRequired | Proxy hop: absolute-form authentication | Userinfo present; ProxyConnectHeader does not cover http:// |
Error text containing socks connect | Proxy hop: SOCKS5 dial or authentication (RFC 1929) | Read the wrapped cause; repeat the checks above |
context deadline exceeded or a Client.Timeout message | Whichever phase was running | Trace the phases; retest once with a justified budget |
tls: failed to verify certificate or another x509 error | Destination hop: TLS | Trust store, such as a missing CA bundle |
| Destination 401, 403 or 429 | Destination hop: HTTP status | Stop; honor Retry-After and the access rules |
To place a deadline, attach an httptrace.ClientTrace to the request context. For an HTTPS destination, ConnectDone reports the gateway dial and TLSHandshakeStart fires only after the gateway processes CONNECT, so a stall between them is a proxy-hop problem. Never set InsecureSkipVerify to make a request pass; the crypto/tls documentation warns that TLS is then susceptible to machine-in-the-middle attacks. To compare outside Go, reproduce one request with the cURL proxy runbook.
Questions specific to Go net/http
Does Go net/http use HTTP_PROXY automatically?
How do I pass proxy credentials to a Go http.Client?
Why does Go report Proxy Authentication Required instead of a 407 response?
Does http.Transport accept socks5:// proxy URLs?
What does context deadline exceeded mean when a Go request uses a proxy?
Should I set InsecureSkipVerify when a proxied Go request fails TLS?
Same gateway, different control surface
Python Requests
HTTP client library
Session -> HTTPAdapter policy -> proxy hop -> approved destination
.NET HttpClient
HTTP client library
HttpClient -> SocketsHttpHandler.Proxy -> CONNECT or SOCKS5 -> observed exit
Java HttpClient
HTTP client library
HttpClient.Builder -> ProxySelector.of -> Authenticator on 407 -> observed exit
PHP Guzzle and cURL
HTTP client library
Guzzle option or CURLOPT -> libcurl handle -> CONNECT or SOCKS5 -> observed exit
Pick the network once the route works. Residential for home-connection exits with state, city, ZIP and ASN targeting; datacenter for fast server IPs on public websites. Switching is one word in the username.
Where teams run Go net/http behind the gateway: Proxies for web scraping · Proxies for market research
Ship Go net/http to production
Create an account, drop in your gateway credentials, and route your first Go net/http request in minutes.
Pricing, order minimums, and traffic validity vary by product.