nats-io/nats-server · error

proxy is configured but remote URL %s requires TLS and no TL

Error message

proxy is configured but remote URL %s requires TLS and no TLS configuration is provided. When using proxy with TLS endpoints, ensure TLS is properly configured for the leafnode remote

What it means

A proxy is configured for a leafnode remote whose URL uses the secure websocket scheme (wss://), but the remote has no TLS configuration (TLSConfig nil and TLS flag false). The CONNECT tunnel through the proxy carries the TLS handshake to the endpoint, so the client side must have TLS enabled to speak wss through it; the server refuses this inconsistent combination at validation time.

Source

Thrown at server/leafnode.go:428

	if remote.Proxy.Timeout < 0 {
		return warnings, fmt.Errorf("proxy timeout must be >= 0")
	}

	if (remote.Proxy.Username == _EMPTY_) != (remote.Proxy.Password == _EMPTY_) {
		return warnings, fmt.Errorf("proxy username and password must both be specified or both be empty")
	}

	if len(remote.URLs) > 0 {
		hasWebSocketURL := false
		hasNonWebSocketURL := false

		for _, remoteURL := range remote.URLs {
			if remoteURL.Scheme == wsSchemePrefix || remoteURL.Scheme == wsSchemePrefixTLS {
				hasWebSocketURL = true
				if (remoteURL.Scheme == wsSchemePrefixTLS) &&
					remote.TLSConfig == nil && !remote.TLS {
					return warnings, fmt.Errorf("proxy is configured but remote URL %s requires TLS and no TLS configuration is provided. When using proxy with TLS endpoints, ensure TLS is properly configured for the leafnode remote", remoteURL.String())
				}
			} else {
				hasNonWebSocketURL = true
			}
		}

		if !hasWebSocketURL {
			warnings = append(warnings, "proxy configuration will be ignored: proxy settings only apply to WebSocket connections (ws:// or wss://), but all configured URLs use TCP connections (nats://)")
		} else if hasNonWebSocketURL {
			warnings = append(warnings, "proxy configuration will only be used for WebSocket URLs: proxy settings do not apply to TCP connections (nats://)")
		}
	}

	return warnings, nil
}

// Wait for the configured reconnect interval before attempting to connect
// again to the remote leafnode.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Add a tls block (or TLSConfig) to the leafnode remote so wss:// can be negotiated
  2. If TLS is not actually wanted, change the remote URL scheme to ws://
  3. Ensure the TLS block certificates are valid so the remote handshake succeeds

Example fix

// before
remotes [
  { url: "wss://leaf.example.com:443"
    proxy { url: "http://proxy:3128" } }
]
// after
remotes [
  { url: "wss://leaf.example.com:443"
    tls { cert_file: "./cert.pem" key_file: "./key.pem" ca_file: "./ca.pem" }
    proxy { url: "http://proxy:3128" } }
]
Defensive patterns

Strategy: validation

Validate before calling

for _, u := range remote.URLs {
    if strings.HasPrefix(u.Scheme, "wss") && remote.Proxy != nil && remote.TLSConfig == nil && !remote.TLS {
        return fmt.Errorf("wss:// remote %s with proxy needs TLS config", u.String())
    }
}

Type guard

func tlsReadyForWssProxy(r *RemoteLeafOpts) bool {
    return r.Proxy == nil || r.TLSConfig != nil || r.TLS
}

Try / catch

if err := server.ProcessConfigFile(conf); err != nil {
    log.Fatalf("config rejected (missing TLS for proxied wss remote?): %v", err)
}

Prevention

When it happens

Trigger: remote.URLs contains a wss:// URL, remote.Proxy is set, remote.TLSConfig is nil and remote.TLS is false — caught in validateLeafNodeProxyOptions during validateLeafNode or parseRemoteLeafNodes.

Common situations: Switching a remote leafnode URL from ws:// to wss:// (e.g. behind a NATS WebSocket gateway) but forgetting to add the tls block; proxy added later without revisiting TLS; config where TLS was assumed inherited but the remote struct has none.

Understand the failure class

Related errors


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