juanfont/headscale · error · errServerURLSuffix
server_url cannot be part of base_domain in a way that could
Error message
server_url cannot be part of base_domain in a way that could make the DERP and headscale server unreachable
What it means
errServerURLSuffix is an unexported config validation error in hscontrol/types/config.go:39 returned by isSafeServerURL (config.go:1342) when the server_url hostname ends with '.'+base_domain (e.g. server_url https://hs.example.com with base_domain example.com). MagicDNS makes the tailnet authoritative for base_domain, so a server_url inside it would make the control plane and DERP unreachable once Tailscale takes over the domain.
Source
Thrown at hscontrol/types/config.go:39
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"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"View on GitHub (pinned to 565fd254d0)
Solutions
- Move server_url to a domain outside base_domain (e.g. serve headscale on headscale.example.net while base_domain is example.com)
- Or change base_domain to a dedicated subdomain zone (e.g. tail.example.com hosts only the tailnet)
- Keep DERP hostnames likewise outside base_domain
- Restart headscale and verify nodes can still resolve and reach the control plane
Example fix
# before server_url: https://headscale.example.com dns: base_domain: example.com # after server_url: https://headscale.example.net dns: base_domain: example.com
Defensive patterns
Strategy: validation
Validate before calling
host := urlHostname(cfg.ServerURL)
if strings.HasSuffix(host, "."+cfg.BaseDomain) {
return errors.New("server_url inside base_domain is unsafe")
} Type guard
null
Try / catch
null
Prevention
- Host headscale and DERP on domains outside dns.base_domain
- Reserve a dedicated zone (subdomain) exclusively for MagicDNS host names
When it happens
Trigger: Setting dns.base_domain to the same registrable domain that hosts the headscale server; e.g. base_domain example.com with server_url https://headscale.example.com; covered by config_test.go cases (lines 167, 549-590).
Common situations: Operators wanting MagicDNS names like machine.example.com on their public domain; small deployments using one domain for everything; following tutorials that suggest putting headscale under the same domain as the tailnet.
Related errors
- server_url cannot use the same domain as base_domain in a wa
- STUN address not set
- initial DERPMap is empty, Headscale requires at least one en
- database type not supported
- failed to allocate IP
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/bacda77be3773427.
Report an issue: GitHub.