juanfont/headscale · error

errTrustedProxyZeroRange

errTrustedProxyZeroRange

Error message

trusted_proxies[%d] %q: 0.0.0.0/0 and ::/0 are not allowed

What it means

Returned when a trusted_proxies entry is 0.0.0.0/0 or ::/0 (prefix length 0). Trusting every possible source defeats the peer-trust gate that validates X-Forwarded-For/X-Real-IP headers, so headscale rejects it as an almost-certain misconfiguration rather than silently trusting spoofable client IPs.

Source

Thrown at hscontrol/types/config.go:1080

}

// trustedProxies rejects 0.0.0.0/0 and ::/0 because they defeat the
// peer-trust gate and almost always indicate misconfiguration.
func trustedProxies() ([]netip.Prefix, error) {
	raw := viper.GetStringSlice("trusted_proxies")
	if len(raw) == 0 {
		return nil, nil
	}

	out := make([]netip.Prefix, 0, len(raw))
	for i, s := range raw {
		p, err := netip.ParsePrefix(s)
		if err != nil {
			return nil, fmt.Errorf("trusted_proxies[%d] %q: %w", i, s, err)
		}

		if p.Bits() == 0 {
			return nil, fmt.Errorf("trusted_proxies[%d] %q: %w", i, s, errTrustedProxyZeroRange)
		}

		out = append(out, p.Masked())
	}

	return out, nil
}

// LoadCLIConfig returns the needed configuration for the CLI client
// of Headscale to connect to a Headscale server.
func LoadCLIConfig() (*Config, error) {
	logConfig := logConfig()
	zerolog.SetGlobalLevel(logConfig.Level)

	return &Config{
		DisableUpdateCheck: viper.GetBool("disable_check_updates"),
		UnixSocket:         viper.GetString("unix_socket"),
		CLI: CLIConfig{

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Restrict trusted_proxies to the actual CIDR(s) your reverse proxy lives in, e.g. 172.16.0.0/12
  2. If the proxy address is dynamic, put it in a dedicated Docker/Kubernetes network and trust that subnet
  3. Remove trusted_proxies entirely and pass the real client IP out-of-band if no fixed proxy subnet exists

Example fix

# before
trusted_proxies:
  - 0.0.0.0/0
# after
trusted_proxies:
  - 172.16.0.0/12
Defensive patterns

Strategy: validation

Validate before calling

import "net/netip"

func assertNoZeroRangeProxies(raw []string) error {
	for i, s := range raw {
		p, err := netip.ParsePrefix(s)
		if err != nil {
			continue // parse error handled separately
		}
		if p.Bits() == 0 {
			return fmt.Errorf("entry %d (%q) is an all-ips range; refuse to trust everyone", i, s)
		}
	}
	return nil
}

Prevention

When it happens

Trigger: Configuring trusted_proxies: ["0.0.0.0/0"] or ["::/0"] and loading the server configuration. Detected via p.Bits() == 0 after successful prefix parsing.

Common situations: Operators copy a permissive allow-all CIDR from other proxy configs (nginx set_real_ip_from style) or try to 'make it work' when their proxy's address keeps changing. It can enable IP spoofing against ACLs or rate limits keyed on client IP.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/62b7d3f5044b7cc2. Report an issue: GitHub.