nats-io/nats-server · error

failed to connect to proxy: %v

Error message

failed to connect to proxy: %v

What it means

The TCP connection to the HTTP proxy server failed during leafnode remote connection. natsDialTimeout could not establish a socket to proxyAddr.Host within the configured proxy timeout, so the CONNECT tunnel cannot even begin. The wrapped error carries the underlying OS/network cause (timeout, connection refused, DNS failure).

Source

Thrown at server/leafnode.go:632

	if s.leafNodeOpts.dialer == nil {
		s.leafNodeOpts.dialer = natsDialTimeout
	}
}

const sharedSysAccDelay = 250 * time.Millisecond

// establishHTTPProxyTunnel establishes an HTTP CONNECT tunnel through a proxy server
func establishHTTPProxyTunnel(proxyURL, targetHost string, timeout time.Duration, username, password string) (net.Conn, error) {
	proxyAddr, err := url.Parse(proxyURL)
	if err != nil {
		// This should not happen since proxy URL is validated during configuration parsing
		return nil, fmt.Errorf("unexpected proxy URL parse error (URL was pre-validated): %v", err)
	}

	// Connect to the proxy server
	conn, err := natsDialTimeout("tcp", proxyAddr.Host, timeout)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to proxy: %v", err)
	}

	// Set deadline for the entire proxy handshake
	if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
		conn.Close()
		return nil, fmt.Errorf("failed to set deadline: %v", err)
	}

	req := &http.Request{
		Method: http.MethodConnect,
		URL:    &url.URL{Opaque: targetHost}, // Opaque is required for CONNECT
		Host:   targetHost,
		Header: make(http.Header),
	}

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

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Verify the proxy is reachable: nc -vz <proxy-host> <proxy-port> from the NATS server host
  2. Correct proxy_url host/port in the leafnode remote config
  3. Increase the proxy timeout value if latency is the issue
  4. Check DNS resolution and firewall/security-group rules for the proxy port

Example fix

// before
proxy {
  url: "http://proxy-wrong-host:3128"
  timeout: 1s
}
// after
proxy {
  url: "http://proxy.internal:3128"
  timeout: 10s
}
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", host, 3*time.Second)
if err != nil { return fmt.Errorf("proxy %s unreachable before start: %w", host, err) }
conn.Close()

Try / catch

var conn net.Conn
err := retry.Do(func() error {
    c, e := establishHTTPProxyTunnel(purl, target, timeout, user, pass)
    if e != nil { return e }
    conn = c
    return nil
}, retry.OnRetry(func(n uint, err error) {
    log.Printf("proxy dial attempt %d failed: %v", n+1, err)
}))

Prevention

When it happens

Trigger: establishHTTPProxyTunnel dials proxyAddr.Host and gets an error: proxy host unreachable, wrong host/port in proxy_url, proxy down, DNS not resolving, firewall dropping, or timeout too small for a slow network.

Common situations: Typos in proxy_url host/port; proxy service not running or listening only on localhost; container/DNS misconfiguration in Kubernetes; corporate firewall blocking the proxy port; proxy timeout set far below actual network latency.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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