nats-io/nats-server · error

proxy CONNECT failed: %s

Error message

proxy CONNECT failed: %s

What it means

The proxy answered the CONNECT request with a non-200 status (e.g. 403 Forbidden, 407 Proxy Authentication Required, 502 Bad Gateway). The tunnel establishment is aborted with the proxy's own status line, so the NATS server cannot reach the target leafnode through the proxy.

Source

Thrown at server/leafnode.go:667

	if username != "" && password != "" {
		req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(username+":"+password)))
	}

	if err := req.Write(conn); err != nil {
		conn.Close()
		return nil, fmt.Errorf("failed to write CONNECT request: %v", err)
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		conn.Close()
		return nil, fmt.Errorf("failed to read proxy response: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		conn.Close()
		return nil, fmt.Errorf("proxy CONNECT failed: %s", resp.Status)
	}

	// Close the response body
	resp.Body.Close()

	// Clear the deadline now that we've finished the proxy handshake
	if err := conn.SetDeadline(time.Time{}); err != nil {
		conn.Close()
		return nil, fmt.Errorf("failed to clear deadline: %v", err)
	}

	return conn, nil
}

// Connect to a remote leaf node asynchronously (that is, this function will do
// the connect in a go routine).
func (s *Server) connectToRemoteLeafNodeAsynchronously(remote *leafNodeCfg, firstConnect bool) {
	remote.setConnectInProgress(true)

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Add matching proxy username/password to the remote config if the status is 407
  2. Check proxy ACLs/firewall rules to allow the target leafnode host:port (403)
  3. Diagnose the proxy→target leg if the status is 502/504 (target down, DNS, egress rules)
  4. Read the %s status string in the error to identify the exact proxy response

Example fix

// before
proxy {
  url: "http://proxy:3128"
}
// after (407 fix)
proxy {
  url: "http://proxy:3128"
  username: "user"
  password: "pass"
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: verify credentials and target reachability via proxy
curl -x http://user:pass@proxy:3128 https://leaf.example.com:443 -I

Try / catch

_, err := establishHTTPProxyTunnel(purl, target, timeout, user, pass)
if err != nil && strings.Contains(err.Error(), "proxy CONNECT failed") {
    switch {
    case strings.Contains(err.Error(), "407"):
        log.Error("proxy needs credentials — set proxy username/password")
    case strings.Contains(err.Error(), "403"):
        log.Error("proxy denies target — update proxy ACLs")
    default:
        log.Errorf("proxy cannot reach target: %v", err)
    }
}

Prevention

When it happens

Trigger: resp.StatusCode != http.StatusOK after http.ReadResponse in establishHTTPProxyTunnel: 407 means missing/wrong proxy credentials; 403 means the proxy forbids the target or source; 502/504 mean the proxy cannot reach the target host.

Common situations: Proxy requiring auth but no username/password configured (407); proxy ACLs blocking the destination host:port (403); target leafnode down or unreachable from the proxy (502/504); corporate proxies whitelisting only certain destinations.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/09c6f9389e470415. Report an issue: GitHub.