{"record":{"id":"eaa8fdd4734afcd2","repo":"grpc/grpc-go","slug":"header-key-exceeds-the-maximum-length-of-d-bytes","errorCode":null,"errorMessage":"header key exceeds the maximum length of %d bytes","messagePattern":"header key exceeds the maximum length of (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/extconfig.go","lineNumber":234,"sourceCode":"}\n\n// validateHeaderKey returns a non-nil error if key may not be mutated by an\n// external processing server, either because the key is reserved or because it\n// is not a valid gRPC header name.\nfunc validateHeaderKey(key string) error {\n\tswitch {\n\tcase len(key) == 0:\n\t\treturn fmt.Errorf(\"header key is empty\")\n\tcase key[0] == ':':\n\t\treturn fmt.Errorf(\"header key %q is a pseudo-header\", key)\n\tcase key == \"host\":\n\t\treturn fmt.Errorf(\"header key %q is reserved\", key)\n\tcase strings.HasPrefix(key, \"grpc-\"):\n\t\treturn fmt.Errorf(\"header key %q is in the reserved 'grpc-' space\", key)\n\tcase key != strings.ToLower(key):\n\t\treturn fmt.Errorf(\"header key %q is not lowercase\", key)\n\tcase len(key) > maxHeaderSize:\n\t\treturn fmt.Errorf(\"header key exceeds the maximum length of %d bytes\", maxHeaderSize)\n\t}\n\treturn imetadata.ValidateKey(key)\n}\n\nfunc (hmr *HeaderMutationRules) allow(key string) bool {\n\tif hmr.DisallowExpr != nil && hmr.DisallowExpr.MatchString(key) {\n\t\treturn false\n\t}\n\tif hmr.AllowExpr != nil && hmr.AllowExpr.MatchString(key) {\n\t\treturn true\n\t}\n\tif hmr.AllowExpr != nil {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// ConstructHeaderMap constructs a HeaderMap from the given metadata and raw","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/httpfilter/extconfig.go#L216-L252","documentation":"Returned by validateHeaderKey when the key length exceeds maxHeaderSize (16384 bytes). Symmetric to the value-size check in ApplyAdditions, but for the key. Matched by len(key) > maxHeaderSize at extconfig.go:233.","triggerScenarios":"The ext_proc server sends a mutation whose header key is longer than 16 KiB. validateHeaderKey fails the size check; in ApplyAdditions this is wrapped as error 441, in ApplyRemovals as error 443.","commonSituations":"Server builds the key from unbounded user input (e.g. a whole URL or JSON blob used as a key); a serialization bug places the value in the key field; server log/metadata leak into the key.","solutions":["Cap header key length on the ext_proc server (well below 16 KiB; realistic keys are < 100 bytes).","Validate the key field is a short identifier before emitting.","If you need a large descriptor, put it in the value (still capped) or the message body.","Add fuzzing on the server's header construction path."],"exampleFix":"// before\nemit(longString, \"v\")\n// after\nconst maxKeyLen = 1024\nif len(longString) > maxKeyLen { return errInvalidKey }\nemit(longString, \"v\")","handlingStrategy":"validation","validationCode":"// server-side: cap key length\nconst maxKeyLen = 1024\nif len(key) > maxKeyLen { return fmt.Errorf(\"header key too long\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Cap key length to a small, realistic limit.","Validate keys are short identifiers, not blobs.","Fuzz header construction with long inputs.","Move large descriptors to the value or body."],"tags":["grpc","xds","extproc","header-mutation","validation"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}