You point a Go service at an API, the call returns 200 OK, and your proxy debugger stays empty. Or you switch the same call to HTTPS and it dies with x509: certificate signed by unknown authority. Neither is a Rockxy bug. Both come from the same root cause: Go's net/http client does not route through a proxy or trust a new root CA unless you tell it to, on the Transport.
The usual reaction is to bury the code in log.Printf lines and guess at what got sent. The outcome of this guide is different: after two small Transport changes, your Go request shows up in Rockxy on macOS with the real method, URL, headers, body, response, and timing — no printf archaeology required.
Why Go traffic skips the proxy
A proxy can only show a request that is actually sent to it. Go's default client often does not send one, for reasons that are all in the client, not the operating system:
- The zero-value client has no proxy set. A bare
http.Client{}orhttp.Getuseshttp.DefaultTransport, whoseProxyfield ishttp.ProxyFromEnvironment. IfHTTP_PROXY/HTTPS_PROXYare unset, the request goes direct and Rockxy sees nothing. - A custom
Transportdefaults to no proxy. The moment you build your own&http.Transport{}— which most real services do, for timeouts or connection pools — itsProxyfield isnilunless you assign it. Even the environment variables stop working. - Loopback is excluded even when a proxy is set.
ProxyFromEnvironmenthonorsNO_PROXY, and Go also treatslocalhostand loopback IPs as no-proxy by default. A request to127.0.0.1is sent direct regardless.
The fix is not to fight the operating system or reach for InsecureSkipVerify. It is to make one Go client send its request through Rockxy on purpose, and — for HTTPS — trust Rockxy's root CA. Rockxy is a local proxy on your Mac, so this is client configuration, not a system-wide change.
What you will need
- Rockxy on macOS 14 (Sonoma) or later. It is a native app; download it from the Rockxy download page.
- A Go toolchain (any recent 1.x) and a small program you can run — the snippets below are self-contained, so a scratch
main.gois enough. - A reachable HTTP endpoint to hit first. A local service on
127.0.0.1:8080works; so does a known external URL. - Two ports clear in your head from the start: the app port (your service, e.g.
8080) and the proxy port (Rockxy's listener, shown in its toolbar). Swapping these two is the single most common mistake.
Step 1 — Confirm Rockxy is capturing and read its proxy port
Start Rockxy and make sure capture is enabled. Read the active proxy port from the toolbar; the examples below assume 8888, but yours may differ. Use whatever value Rockxy shows. If you prefer a guided per-language checklist, the app's Developer Setup Hub lists setup targets and CA export steps — the screenshot further down shows it.
Prove the listener is alive before touching Go. Send one request you know Rockxy should see, straight through the proxy port:
curl -x http://127.0.0.1:8888 https://api.github.com/zen
What you should see in Rockxy: a new row for api.github.com. If even this does not appear, the proxy port is wrong or capture is off — fix that first, because no Go configuration will help until the listener itself receives traffic.
Step 2 — Reproduce the empty capture in Go
Now watch a normal Go call miss the proxy. This is the code most services actually ship — a client with a custom transport and nothing else:
package main
import (
"fmt"
"io"
"net/http"
"time"
)
func main() {
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get("http://127.0.0.1:8080/api/health")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("status=%d\n%s\n", resp.StatusCode, body)
}
It prints status=200, but no row appears in Rockxy. The client never asked for a proxy, so it connected straight to 127.0.0.1:8080. Confirming the miss now means you can trust the fix in the next step.
Step 3 — Route the Go client through Rockxy explicitly
Give the client a Transport with an explicit Proxy. The clearest way is http.ProxyURL with Rockxy's port — it applies to every request, loopback included, with no environment variables in play:
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"time"
)
func main() {
proxyURL, _ := url.Parse("http://127.0.0.1:8888") // Rockxy's toolbar port
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
client := &http.Client{
Transport: transport,
Timeout: 10 * time.Second,
}
resp, err := client.Get("http://127.0.0.1:8080/api/health")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("status=%d\n%s\n", resp.StatusCode, body)
}
http.ProxyURL(proxyURL) forces the request onto Rockxy's proxy port instead of a direct connection, and it does not consult NO_PROXY, so the loopback target is no longer skipped. Rockxy then forwards the request to your service on 8080 and records both sides.
If you would rather drive the proxy from the environment — handy for a service you do not want to recompile — keep ProxyFromEnvironment and set the variables for the run:
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
}
export HTTP_PROXY="http://127.0.0.1:8888"
export HTTPS_PROXY="http://127.0.0.1:8888"
export NO_PROXY=""
go run ./main.go
One catch with the environment path: Go's ProxyFromEnvironment excludes localhost and loopback addresses even when a proxy is set, and it obeys whatever is in NO_PROXY. Clearing NO_PROXY is not always enough for a loopback target, so for a local API prefer the explicit ProxyURL form above. The loopback rule itself is covered in Why Localhost Traffic Isn't Showing in Your Proxy on macOS, and the environment-variable gotchas in Why HTTP_PROXY Environment Variables Aren't Working on macOS.
What success looks like in Rockxy
After Step 3, the traffic list shows a row for 127.0.0.1/api/health. Selecting it reveals GET 200 OK http://127.0.0.1:8080/api/health, the request headers your Go client sent — including the default Go-http-client user agent — the response body, and timing. The screenshot below shows Rockxy's Developer Setup Hub, which lists per-language setup targets and the certificate steps for routing a client like this one through the proxy.
net/http client through the local proxy on 127.0.0.1.This is the baseline. Once one boring Go request is visible, every harder question — wrong base URL, a missing token, a bad JSON shape, a slow route — has far fewer places to hide.
Capturing HTTPS from a Go client
Everything above assumes plain HTTP, which is the right way to prove routing first. When you switch the target to HTTPS, Go verifies the server certificate against the system root store — and Rockxy's on-the-fly certificate is not in it. You get x509: certificate signed by unknown authority.
The correct fix is to trust Rockxy's root CA, not to disable verification. Export Rockxy's root certificate (the certificates and trust docs cover where to find the export), then load that PEM into an x509.CertPool and set it on the transport's TLSClientConfig.RootCAs:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)
func main() {
proxyURL, _ := url.Parse("http://127.0.0.1:8888")
// Start from the system roots so public CAs still verify.
pool, err := x509.SystemCertPool()
if err != nil || pool == nil {
pool = x509.NewCertPool()
}
pem, err := os.ReadFile("/path/to/RockxyRootCA.pem")
if err != nil {
panic(err)
}
if !pool.AppendCertsFromPEM(pem) {
panic("failed to add Rockxy root CA")
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{RootCAs: pool},
}
client := &http.Client{
Transport: transport,
Timeout: 10 * time.Second,
}
resp, err := client.Get("https://api.github.com/zen")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("status=%d\n%s\n", resp.StatusCode, body)
}
If you would rather not touch code, Go's crypto/x509 also reads the SSL_CERT_FILE environment variable, so you can point the whole process at the exported PEM without changing the transport:
export SSL_CERT_FILE="/path/to/RockxyRootCA.pem"
export HTTPS_PROXY="http://127.0.0.1:8888"
go run ./main.go
What you should see in Rockxy: for a decrypted HTTPS request, the selected flow shows a readable request and response body. If you see a CONNECT tunnel-only row instead, the proxy path exists but decryption did not happen — confirm the host is inside Rockxy's SSL Proxying scope and that the CA is loaded. Do not reach for InsecureSkipVerify: true as the normal answer; it hides the exact trust problem you are trying to see, and it disables verification for real endpoints too.
If it does not work
Still no row after setting Proxy. The client is still connecting directly. Check that you assigned the transport to the client (&http.Client{Transport: transport}) and did not fall back to http.Get or http.DefaultClient, which ignore your custom transport entirely.
You see connection refused to the proxy port. The port in ProxyURL does not match Rockxy's listener. Re-read the toolbar value and rerun. This usually means the proxy port drifted after a restart.
The request now fails when it used to succeed. You may have swapped the two ports — pointing ProxyURL at the app port, or the target URL at the proxy. In the examples, 8080 is the service and 8888 is Rockxy. The proxy goes in ProxyURL; the service URL stays as the request target.
x509: certificate signed by unknown authority. That is TLS, not routing. Load Rockxy's root CA into the pool (or set SSL_CERT_FILE) and add the host to SSL Proxying scope. If the target pins its certificate, Rockxy will not silently defeat that — a pinned or failed handshake is surfaced as a failed transaction in the request list so you can see it happened, rather than being decrypted.
Environment variables have no effect. A custom Transport{} with Proxy left nil ignores HTTP_PROXY completely. Either set Proxy: http.ProxyFromEnvironment or use the explicit ProxyURL form.
Limitations worth knowing
Two behaviors here are Go's and the operating system's, not Rockxy's. First, environment-variable proxy support and the loopback bypass are decided by net/http and NO_PROXY parsing — the localhost and environment-variable guides go deeper on why. Second, Rockxy does not bypass certificate pinning: a pinned host shows up as a failed transaction, which is the honest outcome, not decrypted traffic. And Rockxy is a native macOS app — this workflow is for a Go process running on your Mac.
Inspect the captured flow with your AI assistant (optional)
Once the Go request is captured, you can let an MCP-capable AI tool read it directly instead of pasting terminal output into a chat. Rockxy ships a built-in local MCP server that exposes read and replay tools — list_flows, get_flow_detail, replay_request, and diff_flows — so an assistant like Claude or Cursor can ask "what did that Go client actually send?" against the real capture.
Local MCP is 100% free and unlimited in the Free tier. No license upgrade required. The workflow is local-first and user-controlled: Rockxy's MCP server binds to localhost, and captured data stays in a local store on your Mac. Rockxy itself never uploads your traffic to an AI provider. If you enable the workflow, the assistant you connect may include the data it retrieves in its own prompts — that is your choice to make per tool. Setup is in the Connect Claude Desktop to Rockxy guide.
Next steps
The Go rule is short: net/http routes and trusts only what its Transport says to. Set Proxy to http.ProxyURL so the request reaches Rockxy, add Rockxy's root CA to RootCAs when you move to HTTPS, and your Go request becomes a normal captured flow.
From here, compare notes with the Node.js setup if your stack is polyglot, revisit the localhost routing and proxy environment-variable guides when a request refuses to show, or read the certificates and trust docs for the CA export details. When you are ready, download Rockxy and capture your first Go flow.