juanfont/headscale · error · errTrustedProxyZeroRange

0.0.0.0/0 and ::/0 are not allowed

Error message

0.0.0.0/0 and ::/0 are not allowed

What it means

errTrustedProxyZeroRange is a sentinel error in hscontrol/types/config.go that rejects the catch-all CIDRs 0.0.0.0/0 and ::/0 in the trusted_proxies config list. Headscale trusts proxies listed there to supply the real client IP via X-Forwarded-For / X-Real-Ip headers; trusting every address means any client can spoof its IP and bypass source-IP checks. It is returned at config load time (config.go:1080, wrapped as `trusted_proxies[%d] %q: ...`), so the server refuses to start.

Source

Thrown at hscontrol/types/config.go:42

	"tailscale.com/util/set"
)

const (
	PKCEMethodPlain string = "plain"
	PKCEMethodS256  string = "S256"

	defaultNodeStoreBatchSize = 100
)

var (
	errOidcMutuallyExclusive     = errors.New("oidc_client_secret and oidc_client_secret_path are mutually exclusive")
	errOIDCIssuerInvalid         = errors.New("oidc.issuer must be a valid http(s) URL")
	errOIDCClientIDRequired      = errors.New("oidc.client_id is required when oidc.issuer is set")
	errOIDCClientSecretRequired  = errors.New("oidc.client_secret or oidc.client_secret_path is required when oidc.issuer is set")
	errServerURLSuffix           = errors.New("server_url cannot be part of base_domain in a way that could make the DERP and headscale server unreachable")
	errServerURLSame             = errors.New("server_url cannot use the same domain as base_domain in a way that could make the DERP and headscale server unreachable")
	errInvalidPKCEMethod         = errors.New("pkce.method must be either 'plain' or 'S256'")
	errTrustedProxyZeroRange     = errors.New("0.0.0.0/0 and ::/0 are not allowed")
	ErrNoPrefixConfigured        = errors.New("no IPv4 or IPv6 prefix configured, minimum one prefix is required")
	ErrInvalidAllocationStrategy = errors.New("invalid prefix allocation strategy")
)

type IPAllocationStrategy string

const (
	IPAllocationStrategySequential IPAllocationStrategy = "sequential"
	IPAllocationStrategyRandom     IPAllocationStrategy = "random"
)

type PolicyMode string

const (
	PolicyModeDB   = "database"
	PolicyModeFile = "file"
)

View on GitHub (pinned to 565fd254d0)

Solutions

  1. List only the specific reverse-proxy addresses/CIDRs, e.g. trusted_proxies: ["127.0.0.1/32", "192.168.1.0/24"]
  2. If headscale is directly exposed with no reverse proxy, remove trusted_proxies entirely
  3. Run `headscale config verify` (or start the server) after editing to confirm the config passes validation

Example fix

# before
trusted_proxies:
  - 0.0.0.0/0

# after (only the actual proxy)
trusted_proxies:
  - 127.0.0.1/32
  - 172.17.0.0/16
Defensive patterns

Strategy: validation

Validate before calling

// before starting headscale, sanity-check the config value
for _, p := range cfg.TLS.TrustedProxies { // or your trusted_proxies slice
	parsed, err := netip.ParsePrefix(p)
	if err != nil {
		return fmt.Errorf("bad trusted_proxies entry %q: %w", p, err)
	}
	if parsed == netip.MustParsePrefix("0.0.0.0/0") || parsed == netip.MustParsePrefix("::/0") {
		return fmt.Errorf("trusted_proxies must not contain catch-all ranges, got %q", p)
	}
}

Prevention

When it happens

Trigger: Setting trusted_proxies: ["0.0.0.0/0"] or ["::/0"] in config.yaml and running `headscale serve`; LoadConfig parses each entry with netip.ParsePrefix and hits the zero-range check at hscontrol/types/config.go:1080.

Common situations: Copying a reverse-proxy example config and pasting 0.0.0.0/0 to 'trust everything'; upgrading from an older headscale that silently tolerated it; misunderstanding the option as an ACL allowlist instead of an HTTP proxy trust list.

Related errors


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