{"record":{"id":"c31cee34745669ac","repo":"gastownhall/beads","slug":"allowed-host-q-carries-a-port-the-port-is-stri","errorCode":null,"errorMessage":"--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","messagePattern":"--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","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/httpapi/server.go","lineNumber":1875,"sourceCode":"func 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\n}\n\n// allows reports whether a Host header value is one this server answers to.\nfunc (p hostPolicy) allows(host string) bool {\n\th := hostOnly(host)\n\tif p.names[h] {\n\t\treturn true\n\t}\n\tip := net.ParseIP(h)\n\tif ip == nil {\n\t\treturn false\n\t}\n\treturn p.anyIP || containsIP(p.ips, ip)\n}\n\n// label renders the policy for the startup line, so an operator can read what","sourceCodeStart":1857,"sourceCodeEnd":1893,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/httpapi/server.go#L1857-L1893","documentation":"ValidateAllowedHost rejects entries carrying a port (a ':' after the host, excluding bracketed IPv6). The server strips the port from a request's Host header before matching, so an allow-list entry with a port could never match — the flag refuses it rather than silently never matching.","triggerScenarios":"Calling ValidateAllowedHost with \"bd.example.com:8080\" or \"example.com:443\" (non-IP values containing ':'). Bracketed IPv6 like [::1] is accepted because ParseIP succeeds first.","commonSituations":"Operators copy the host:port they use in the client's base URL into --allowed-host without realizing matching is port-agnostic.","solutions":["Remove the port and pass only the host: --allowed-host bd.example.com","For IPv6 loopback use bracket form: --allowed-host \"[::1]\"","Rely on the client to send Host with or without port; matching ignores the port"],"exampleFix":"// before\n--allowed-host bd.example.com:8080\n// after\n--allowed-host bd.example.com","handlingStrategy":"validation","validationCode":"if i := strings.LastIndex(v, \":\"); i >= 0 && !strings.HasSuffix(v, \"]\") {\n    v = strings.Trim(v[:i], \"[]\") // strip port for bracketed IPv6\n}","typeGuard":null,"tryCatchPattern":"if err := httpapi.ValidateAllowedHost(v); err != nil {\n    log.Fatalf(\"invalid --allowed-host: %v\", err)\n}","preventionTips":["Omit the port from allowed-host entries — matching is port-agnostic","Use bracketed [::1] form for IPv6","Remember the Host header port is stripped before matching"],"tags":["validation","cli","http","host-header"],"backgroundTag":"invalid-allowed-host","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}