{"record":{"id":"e55a795c49867fe1","repo":"snail007/goproxy","slug":"can-not-find-host-header","errorCode":null,"errorMessage":"can not find HOST header","messagePattern":"can not find HOST header","errorType":"http","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"utils/structs.go","lineNumber":365,"sourceCode":"\t}\n\tURL = fmt.Sprintf(\"http://%s%s\", _host, req.hostOrURL)\n\treturn\n}\nfunc (req *HTTPRequest) getHeader(key string) (val string, err error) {\n\tkey = strings.ToUpper(key)\n\tlines := strings.Split(string(req.HeadBuf), \"\\r\\n\")\n\tfor _, line := range lines {\n\t\tline := strings.SplitN(strings.Trim(line, \"\\r\\n \"), \":\", 2)\n\t\tif len(line) == 2 {\n\t\t\tk := strings.ToUpper(strings.Trim(line[0], \" \"))\n\t\t\tv := strings.Trim(line[1], \" \")\n\t\t\tif key == k {\n\t\t\t\tval = v\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\terr = fmt.Errorf(\"can not find HOST header\")\n\treturn\n}\n\nfunc (req *HTTPRequest) addPortIfNot() (newHost string) {\n\t//newHost = req.Host\n\tport := \"80\"\n\tif req.IsHTTPS() {\n\t\tport = \"443\"\n\t}\n\tif (!strings.HasPrefix(req.Host, \"[\") && strings.Index(req.Host, \":\") == -1) || (strings.HasPrefix(req.Host, \"[\") && strings.HasSuffix(req.Host, \"]\")) {\n\t\t//newHost = req.Host + \":\" + port\n\t\t//req.headBuf = []byte(strings.Replace(string(req.headBuf), req.Host, newHost, 1))\n\t\treq.Host = req.Host + \":\" + port\n\t}\n\treturn\n}\n\ntype OutPool struct {","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/snail007/goproxy/blob/e6d6a821db80e7f47ee6e981a144984e1d4ddb3d/utils/structs.go#L347-L383","documentation":"getHeader scans the parsed request head (HeadBuf split on \\r\\n) for a header key case-insensitively. When the requested header is absent it returns the (misleadingly static) error \"can not find HOST header\", regardless of which key was requested. Its callers are BasicAuth (Authorization) and getHTTPURL (host); the message text just isn't parameterized.","triggerScenarios":"Two distinct calls: (a) getHTTPURL calls getHeader(\"host\") for a relative-form request target (path-only URL like \"GET /path\") when the client omitted the Host header; (b) BasicAuth calls getHeader(\"Authorization\") when the client sent no Authorization header (in which case BasicAuth first sends 401 + WWW-Authenticate and closes, surfacing this error to the caller).","commonSituations":"HTTP/1.0 clients that legitimately omit Host; hand-rolled HTTP clients missing the header; requests to an absolute URL while basic auth expects an Authorization header that the client didn't send yet (first request without preemptive auth); curl in --http1.0 mode; scrapers/health checks not sending Host.","solutions":["If it's the Host case, configure clients to send HTTP/1.1 requests with a Host header (curl does this by default; check for --http1.0 flags or custom sockets).","If it's the Authorization case, respond to the 401 challenge: the client should resend with credentials (curl: `-U user:pass` / Go: req.SetBasicAuth) — preemptive auth avoids the round trip.","Note the error message is static even for missing Authorization headers — check which header your call path needs before debugging based on the message text alone.","Improve the error for diagnosability by including the requested key: `fmt.Errorf(\"can not find header %s\", key)` (library-side patch)."],"exampleFix":"// before\nerr = fmt.Errorf(\"can not find HOST header\")\n\n// after\nerr = fmt.Errorf(\"can not find header %q in request: %.50s\", key, string(req.HeadBuf))","handlingStrategy":"type-guard","validationCode":"// ensure required headers are present before sending the request through the proxy\nif req.URL.Path != \"\" && req.Host == \"\" {\n\treturn errors.New(\"relative-form target requires a Host header (use HTTP/1.1)\")\n}\nif needsAuth && req.Header.Get(\"Authorization\") == \"\" {\n\treturn errors.New(\"proxy requires basic auth; set Authorization preemptively\")\n}","typeGuard":"func hasHeader(headBuf []byte, key string) bool {\n\twant := strings.ToUpper(key)\n\tfor _, line := range strings.Split(string(headBuf), \"\\r\\n\") {\n\t\tparts := strings.SplitN(line, \":\", 2)\n\t\tif len(parts) == 2 && strings.ToUpper(strings.TrimSpace(parts[0])) == want {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","tryCatchPattern":"_, err := utils.NewHTTPRequest(conn, bufSize, true, auth)\nif err != nil && strings.Contains(err.Error(), \"can not find HOST header\") {\n\t// message is static for ANY missing header — check whether it was Host or Authorization\n\tlog.Println(\"request missing a required header (Host or Authorization)\")\n\t// for Authorization: resend with credentials after the 401 challenge\n}","preventionTips":["Always use HTTP/1.1 on the client so the Host header is sent (avoid --http1.0).","Use absolute-URI request targets or ensure Host is present for path-form targets.","Send credentials preemptively (curl -U / req.SetBasicAuth) to skip the 401 challenge round trip.","Remember the message text is the same for any missing header — inspect the call path, not just the message."],"tags":["http","headers","host-header","basic-auth","go"],"backgroundTag":"missing-host-header","analyzedSha":"e6d6a821db80e7f47ee6e981a144984e1d4ddb3d","analyzedAt":"2026-09-03T15:32:42.750Z","contentChangedAt":"2026-09-03T15:32:42.750Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}