Example: HTTP Client Use a Proxy
An HTTP proxy sits between your Go program and the server it wants to reach.
The basic path is:
In Go, proxy configuration belongs to the http.Transport, not the http.Client.
A useful mental model is:
http.Clientcontrols request behavior: timeouts, redirects, and cookies.http.Transportcontrols connections: dialing, connection pooling, TLS, and proxies.
Once you understand that distinction, proxy configuration becomes much easier to reason about.
Quick Example
For most applications, let the deployment environment decide whether a proxy is used:
In a real application, return errors instead of calling panic. The example uses panic only to keep the setup short.
Go's ProxyFromEnvironment reads HTTP_PROXY, HTTPS_PROXY, and NO_PROXY, including their lowercase forms. HTTPS_PROXY is used for HTTPS requests, while NO_PROXY can bypass the proxy for selected destinations.
This is usually the simplest production setup because the same binary can run:
without changing application code.
1. Environment Proxies
The standard environment variables are:
Lowercase forms are also recognized:
If both uppercase and lowercase versions are present, the lowercase values take precedence in the current proxy configuration implementation.
For example:
Then:
is enough.
When this is a good choice
Environment configuration works well when proxy selection belongs to the deployment environment:
- corporate networks
- Kubernetes deployments
- CI runners
- development environments
- services with the same network policy across many destinations
The application does not need to know the actual proxy address.
NO_PROXY
NO_PROXY tells Go which destinations should bypass the proxy.
For example:
A domain without a leading dot can match the domain and its subdomains. A leading dot is used when you specifically want subdomains.
Ports can also be included:
That applies to port 8443; it does not mean that every port on example.com bypasses the proxy.
A single * disables proxying for all destinations.
Do not treat NO_PROXY as a security mechanism. It controls routing, not authorization.
2. Fixed Proxies
Sometimes the application itself must choose the proxy.
For example:
- a crawler rotates between known proxies
- different customers use different network paths
- a security tool has an explicit egress proxy
- a service has a fixed corporate proxy
Parse the proxy URL during initialization:
http.ProxyURL returns a proxy function that always uses that URL.
Do not create a new Transport for every request. The transport owns connection pools and other connection state.
Create it once and reuse it.
3. What Actually Happens When You Use a Proxy?
The behavior depends on the protocol.
HTTP
For a plain HTTP request, the client sends the request through the proxy:
The proxy can see the HTTP request because the request is not encrypted by TLS.
HTTPS
HTTPS is different.
The client first asks the proxy to create a TCP tunnel:
The proxy connects to:
and establishes a tunnel.
Then the TLS handshake happens through that tunnel:
The important point is that an ordinary HTTP proxy does not automatically terminate HTTPS.
This distinction is extremely useful when debugging.
If the proxy refuses the CONNECT, the origin server may never see the request.
4. SOCKS5
Go's http.Transport also supports SOCKS proxies.
For example:
The current standard library accepts:
and treats them the same.
This is worth knowing because other SOCKS clients may distinguish:
That distinction is common in other tools and libraries, but it should not be assumed when reading Go's standard net/http behavior.
If you use a third-party SOCKS library, check that library's documentation separately.
5. Proxy Authentication
A proxy may require authentication.
The simplest form is putting credentials in the proxy URL:
Go uses the credentials to construct the proxy authentication information.
Do not log the proxy URL
This is dangerous:
A proxy URL containing credentials can expose the password in:
- logs
- traces
- metrics
- error messages
- debugging output
Treat proxy credentials like any other secret.
Also remember that:
authenticates the client to the proxy.
It is different from:
which normally authenticates the client to the origin server.
6. Dynamic Authentication for HTTPS Proxies
Some proxies require short-lived credentials for HTTPS CONNECT requests.
Use GetProxyConnectHeader when the authentication value must be generated dynamically:
The callback runs as part of establishing the proxy connection.
That means this is a bad place for slow work such as:
If many requests need new connections, slow authentication work can delay connection establishment and consume connection resources.
Prefer to obtain short-lived credentials ahead of time and keep the callback cheap.
For example:
The callback also receives a context, so any unavoidable work should respect cancellation and deadlines.
7. Different Proxies for Different Destinations
Sometimes one client needs more than one proxy.
For example:
Transport.Proxy is a function:
So routing can be based on the request:
The important part is that the callback stays cheap.
Do not do this:
The proxy function participates in connection setup. Parse configuration and load routing information during initialization whenever possible.
8. A Proxy Changes Your Network Boundary
A proxy is not just a different URL.
It changes where the network connection originates.
Without a proxy:
With a proxy:
That matters for security.
SSRF
Suppose your application accepts a user-controlled URL:
An attacker might try to make the service request an internal address such as:
or another private or loopback address.
With a proxy, the request may originate from the proxy's network rather than directly from the application host.
Therefore, if your application fetches user-controlled URLs:
- validate the URL scheme
- validate the destination hostname
- resolve and validate destination IPs
- reject private, loopback, link-local, and other forbidden ranges as appropriate
- enforce egress policy at the proxy or network layer
- account for redirects and subsequent DNS resolution
A single DNS lookup before the request is not a complete defense against DNS rebinding. The address used for validation and the address eventually used for connection establishment can differ.
The proxy itself must also be part of the threat model.
9. Redirects Can Cross Network Boundaries
Proxy security does not stop at the first URL.
Consider:
Your application may start with a harmless public URL but eventually follow a redirect to another destination.
If the application accepts untrusted URLs, apply your SSRF policy to the redirect destination too.
This is one reason URL validation should be treated as a request policy rather than a one-time string check.
10. Do Not Create a Transport Per Request
Avoid this pattern:
The code works, but the lifecycle is wrong for a normal long-running application.
A Transport maintains connection state and connection pools.
Create it once:
Then reuse the client:
This allows the transport to reuse connections instead of rebuilding the network path for every request.
11. Troubleshooting Proxy Errors
When a proxied request fails, first identify which network hop failed.
Think in this order:
proxyconnect tcp: dial tcp ... connection refused
The application could not establish a TCP connection to the proxy.
Check:
- proxy hostname
- proxy port
- DNS
- firewall rules
- whether the proxy is running
The proxyconnect tcp prefix is a useful clue: the failure happened while connecting to the proxy, before reaching the origin.
407 Proxy Authentication Required
The proxy rejected authentication.
Check:
- username/password
Proxy-Authorization- dynamic CONNECT headers
- URL encoding of credentials
For example, passwords containing characters such as:
must be encoded correctly when placed in a URL.
403 during CONNECT
The proxy rejected the tunnel request.
For example:
The origin server may never have received the request.
Common causes include proxy ACLs that restrict:
- destination domains
- destination ports
- HTTPS tunneling
x509: certificate signed by unknown authority
If the request works directly but fails through the proxy, the proxy may be performing TLS interception.
The path can become:
The proxy terminates the first TLS connection and creates another one to the origin.
Your Go client may therefore receive a certificate signed by the organization's internal CA.
The correct fix is to trust the organization's CA when that TLS interception is intentional.
Do not solve this with:
That disables certificate verification.
NO_PROXY is not working
Check the exact destination host and port.
For example:
does not mean:
also bypasses the proxy.
Also remember that:
and:
have different matching semantics.
When debugging, log the destination host and port—not credentials or proxy URLs containing secrets.
Common Mistakes
Mistake 1: Configuring the proxy on http.Client
Proxy configuration belongs to:
not directly to:
The client owns request-level behavior; the transport owns connection behavior.
Mistake 2: Creating a new transport for every request
This prevents effective connection reuse.
Reuse the transport and client.
Mistake 3: Logging proxy credentials
Avoid logging:
when the URL contains user information.
Credentials can also leak through traces, metrics, and error messages.
Mistake 4: Using InsecureSkipVerify to fix proxy TLS errors
A corporate TLS interception problem should be solved by configuring the correct trust chain, not by disabling certificate verification.
Mistake 5: Doing slow work inside Transport.Proxy
The proxy callback participates in connection selection.
Keep it deterministic and cheap.
Mistake 6: Assuming the proxy is only a performance feature
A proxy changes the network boundary.
It can affect:
- DNS behavior
- source network identity
- TLS interception
- access control
- SSRF exposure
- routing policy
Code Review
Before shipping proxy-enabled HTTP code, check:
-
TransportandClientare reused -
Client.Timeoutor request context provides a deadline - proxy configuration is explicit
-
NO_PROXYbehavior is understood - proxy credentials are not logged
- dynamic proxy authentication respects context
-
Transport.Proxydoes not perform slow I/O - HTTPS
CONNECTbehavior is understood - TLS interception is handled through the correct CA
-
InsecureSkipVerifyis not used as a shortcut - user-controlled URLs have SSRF protection
- redirect destinations are covered by the same security policy
- DNS rebinding is considered when validating destinations
- proxy-level egress restrictions are part of the security design
Engineering Rule
Start with the simplest model:
Use ProxyFromEnvironment when proxy routing belongs to deployment configuration.
Use ProxyURL or a custom Transport.Proxy function when the application owns the routing decision.
For HTTPS, remember that an ordinary HTTP proxy usually creates a CONNECT tunnel rather than seeing the decrypted request.
And when a proxy is involved in security-sensitive traffic, treat it as part of your application's network boundary—not as a transparent pipe.