{"record":{"id":"6346148c02a19337","repo":"valyala/fasthttp","slug":"invalid-port-q-after-host","errorCode":null,"errorMessage":"invalid port %q after host","messagePattern":"invalid port %q after host","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"uri.go","lineNumber":441,"sourceCode":"}\n\n// parseHost parses host as an authority without user\n// information. That is, as host[:port].\n//\n// Based on https://github.com/golang/go/blob/8ac5cbe05d61df0a7a7c9a38ff33305d4dcfea32/src/net/url/url.go#L619\n//\n// The host is parsed and unescaped in place overwriting the contents of the host parameter.\nfunc parseHost(host []byte) ([]byte, error) {\n\tif len(host) > 0 && host[0] == '[' {\n\t\t// Parse an IP-Literal in RFC 3986 and RFC 6874.\n\t\t// E.g., \"[fe80::1]\", \"[fe80::1%25en0]\", \"[fe80::1]:80\".\n\t\ti := bytes.LastIndexByte(host, ']')\n\t\tif i < 0 {\n\t\t\treturn nil, errors.New(\"missing ']' in host\")\n\t\t}\n\t\tcolonPort := host[i+1:]\n\t\tif !validOptionalPort(colonPort) {\n\t\t\treturn nil, fmt.Errorf(\"invalid port %q after host\", colonPort)\n\t\t}\n\n\t\t// RFC 6874 defines that %25 (%-encoded percent) introduces\n\t\t// the zone identifier, and the zone identifier can use basically\n\t\t// any %-encoding it likes. That's different from the host, which\n\t\t// can only %-encode non-ASCII bytes.\n\t\t// We do impose some restrictions on the zone, to avoid stupidity\n\t\t// like newlines.\n\t\tzone := bytes.Index(host[:i], []byte(\"%25\"))\n\t\tif zone >= 0 {\n\t\t\thost1, err := unescape(host[:zone], encodeHost)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\thost2, err := unescape(host[zone:i], encodeZone)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/valyala/fasthttp/blob/c96f600972c6f4a7a30d664257b340ebe9d60124/uri.go#L423-L459","documentation":"When parsing the host portion of a URI, parseHost checks that any port following a bracketed IPv6 literal (text after the last ']') is a valid optional port via validOptionalPort (either empty or ':' + digits). If not, it returns \"invalid port %q after host\". This guards URIs like \"[::1]:abc\".","triggerScenarios":"URI.Parse on URIs such as \"http://[::1]:8o80/\", \"//[::1]:x/\", or any host in bracket form whose suffix after ']' is not ':' followed by digits only.","commonSituations":"Proxies forwarding malformed targets from clients; log/URL reconstruction tools that truncate or corrupt ports; hand-assembled URLs with typos in the port.","solutions":["Validate/normalize the port as numeric (1-65535) before parsing","Use net/url.Parse for friendlier upstream validation, or reject the input at the edge","Return 400 for client-supplied malformed targets in server code","Strip or correct the port and re-attempt the parse if a default port applies"],"exampleFix":"// before\nvar u uri.URI\nu.Parse(nil, nil, []byte(\"http://[::1]:8o80/\")) // invalid port \"o80\"\n// after\nport := 8_080 // normalize to numeric\nvar u uri.URI\nu.Parse(nil, nil, []byte(fmt.Sprintf(\"http://[::1]:%d/\", port)))","handlingStrategy":"validation","validationCode":"func portSuffixValid(host string) bool {\n    if i := strings.LastIndexByte(host, ']'); i >= 0 {\n        p := host[i+1:]\n        if p == \"\" { return true }\n        if p[0] != ':' { return false }\n        for _, c := range p[1:] {\n            if c < '0' || c > '9' { return false }\n        }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":"var u uri.URI\nif err := u.Parse(nil, nil, raw); err != nil {\n    if strings.Contains(err.Error(), \"invalid port\") {\n        return fmt.Errorf(\"bad port in target: %w\", err) // reject with 400\n    }\n    return err\n}","preventionTips":["Validate ports are integers in 1-65535 before constructing URLs","Beware locale/OCR-corrupted digits (e.g. 'o' vs '0') in copied URLs","Test IPv6-literal targets with ports in your URL handling tests"],"tags":["uri","parsing","port","ipv6","validation"],"backgroundTag":"invalid-uri-port","analyzedSha":"c96f600972c6f4a7a30d664257b340ebe9d60124","analyzedAt":"2026-08-31T22:48:28.265Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}