{"record":{"id":"af53741468ee873d","repo":"netbirdio/netbird","slug":"unauthorized-af5374","errorCode":null,"errorMessage":"unauthorized","messagePattern":"unauthorized","errorType":"http","errorClass":null,"httpStatus":401,"severity":"warning","filePath":"upload-server/server/server.go","lineNumber":86,"sourceCode":"\nfunc getObjectKey(w http.ResponseWriter, r *http.Request) string {\n\tid := r.URL.Query().Get(\"id\")\n\tif id == \"\" {\n\t\thttp.Error(w, \"id query param required\", http.StatusBadRequest)\n\t\treturn \"\"\n\t}\n\n\treturn id + \"/\" + uuid.New().String()\n}\n\nfunc isValidRequest(w http.ResponseWriter, r *http.Request) bool {\n\tif r.Method != http.MethodGet {\n\t\thttp.Error(w, \"method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn false\n\t}\n\n\tif r.Header.Get(types.ClientHeader) != types.ClientHeaderValue {\n\t\thttp.Error(w, \"unauthorized\", http.StatusUnauthorized)\n\t\treturn false\n\t}\n\treturn true\n}\nfunc respondGetRequest(w http.ResponseWriter, uploadURL string, objectKey string) {\n\tresponse := types.GetURLResponse{\n\t\tURL: uploadURL,\n\t\tKey: objectKey,\n\t}\n\n\trdata, err := json.Marshal(response)\n\tif err != nil {\n\t\thttp.Error(w, \"failed to marshal response\", http.StatusInternalServerError)\n\t\tlog.Errorf(\"Marshal error: %v\", err)\n\t\treturn\n\t}\n\n\tw.WriteHeader(http.StatusOK)","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/upload-server/server/server.go#L68-L104","documentation":"HTTP 401 from isValidRequest when the request lacks the x-nb-client header or its value is not exactly 'netbird' (types.ClientHeader / types.ClientHeaderValue). This is a light client-identification gate, not authentication: the header value must simply match exactly.","triggerScenarios":"GET /upload-url without the header; header present but value mismatched ('NetBird', 'netbird/1.0', trailing whitespace); a proxy or CORS layer stripping custom x-nb-* headers.","commonSituations":"curl/fetch test calls that forget the header; intermediaries normalizing or dropping unknown headers; header name case is fine (HTTP headers are case-insensitive) but the value comparison is exact.","solutions":["Add the header: x-nb-client: netbird (exact value)","If a proxy is in the path, verify it forwards custom request headers","Remember this is identification, not auth: do not expose the endpoint publicly without a real auth layer in front"],"exampleFix":"# before -> 401 unauthorized\ncurl 'https://srv/upload-url?id=x'\n\n# after\ncurl 'https://srv/upload-url?id=x' -H 'x-nb-client: netbird'","handlingStrategy":"validation","validationCode":"req.Header.Set(\"x-nb-client\", \"netbird\")\n// or, shared with server code:\nreq.Header.Set(types.ClientHeader, types.ClientHeaderValue)","typeGuard":null,"tryCatchPattern":"On 401 'unauthorized', verify the x-nb-client header is present with the exact value 'netbird' (no version suffix, no whitespace), then retry once; a persistent 401 means a proxy is stripping the header.","preventionTips":["Centralize header injection in one HTTP client wrapper so no call site forgets it","Check that corporate proxies/CORS configurations forward custom x-nb-* headers","Do not treat this header as security; put real auth in front of public deployments"],"tags":["http","unauthorized","headers","upload","go"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}