{"record":{"id":"13f850e6512fad21","repo":"gastownhall/beads","slug":"allowed-host-is-empty-pass-the-host-header-valu","errorCode":null,"errorMessage":"--allowed-host is empty; pass the Host header value clients send, such as bd-myproject.beads.svc.cluster.local","messagePattern":"--allowed-host is empty; pass the Host header value clients send, such as bd-myproject\\.beads\\.svc\\.cluster\\.local","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/httpapi/server.go","lineNumber":1859,"sourceCode":"\t\t\tif !containsIP(p.ips, ip) {\n\t\t\t\tp.ips = append(p.ips, ip)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tp.names[h] = true\n\t}\n\treturn p\n}\n\n// ValidateAllowedHost refuses an allowlist entry that is not a bare host.\n//\n// The Host header's port is stripped before matching (hostOnly), so an entry\n// carrying one would silently never match — and an operator who wrote it would\n// reasonably read the startup line as proof that it does. A URL, a path or\n// embedded whitespace is the same mistake in a louder form.\nfunc ValidateAllowedHost(v string) error {\n\tif strings.TrimSpace(v) == \"\" {\n\t\treturn errors.New(\"--allowed-host is empty; pass the Host header value clients send, such as bd-myproject.beads.svc.cluster.local\")\n\t}\n\tif strings.ContainsAny(v, \" \\t\\r\\n\") {\n\t\treturn fmt.Errorf(\"--allowed-host %q contains whitespace; it must be a bare host name or IP\", v)\n\t}\n\tif strings.ContainsAny(v, \"/@\") {\n\t\treturn fmt.Errorf(\"--allowed-host %q looks like a URL; pass just the host, with no scheme and no path\", v)\n\t}\n\t// An IPv6 address is spelled in brackets in a Host header, so an operator\n\t// copying one off the wire types it that way. hostOnly strips them before\n\t// matching, so the entry works; refusing it here — with a message about a\n\t// port it does not have — would be the validation lying about the policy.\n\tif net.ParseIP(strings.TrimSuffix(strings.TrimPrefix(v, \"[\"), \"]\")) != nil {\n\t\treturn nil\n\t}\n\tif strings.Contains(v, \":\") {\n\t\treturn fmt.Errorf(\"--allowed-host %q carries a port; the port is stripped from a request's Host before matching, so an entry with one could never match\", v)\n\t}\n\treturn nil","sourceCodeStart":1841,"sourceCodeEnd":1877,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/httpapi/server.go#L1841-L1877","documentation":"ValidateAllowedHost rejects an --allowed-host value that is empty after trimming. Allowed hosts are matched against the incoming request's Host header (with the port stripped by hostOnly), so an empty entry can never match and the server would reject every client. The error names the exact thing to pass: the bare Host header value clients send.","triggerScenarios":"Starting the server with --allowed-host=\"\" or only whitespace; programmatically calling ValidateAllowedHost(\"\") or with a value of only spaces/tabs before saving an allowed-host entry.","commonSituations":"An empty environment variable or config default silently substituted for the flag; templated deploy configs where the host placeholder resolved to empty; forgetting the flag entirely and defaulting to empty string.","solutions":["Pass the actual Host header value clients use, e.g. --allowed-host bd-myproject.beads.svc.cluster.local (no scheme, no path, no port)","Fix the surrounding config/secret/templating so the value is not empty at startup","If multiple hosts are allowed, pass each as its own non-empty --allowed-host entry"],"exampleFix":"// before\nbd serve --allowed-host=\"\"\n// after\nbd serve --allowed-host=bd-myproject.beads.svc.cluster.local","handlingStrategy":"validation","validationCode":"v := strings.TrimSpace(flagAllowedHost)\nif v == \"\" {\n    return errors.New(\"--allowed-host must be the Host header value clients send, e.g. bd-myproject.beads.svc.cluster.local\")\n}\nif err := httpapi.ValidateAllowedHost(v); err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail fast at deploy time: validate config templates render a non-empty allowed-host before starting the server","Never pass scheme, path, or port — store only bare host names in config","Add a CI check running httpapi.ValidateAllowedHost over all environment configs"],"tags":["httpapi","configuration","security","allowed-host"],"backgroundTag":"invalid-allowed-host","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}