{"record":{"id":"7fde2a2d20cede66","repo":"caddyserver/caddy","slug":"s-entry-q-has-in-an-invalid-position-only","errorCode":null,"errorMessage":"%s: entry %q has '*' in an invalid position (only a trailing '*' is allowed)","messagePattern":"(.+?): entry %q has '\\*' in an invalid position \\(only a trailing '\\*' is allowed\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/server.go","lineNumber":406,"sourceCode":"\n\tfor _, entry := range entries {\n\t\t// Reject non-ASCII bytes: Go's HTTP parser returns 400 for\n\t\t// non-ASCII header names, so such entries can never match.\n\t\tfor i := 0; i < len(entry); i++ {\n\t\t\tif entry[i] >= 0x80 {\n\t\t\t\treturn nil, nil, nil, fmt.Errorf(\"%s: entry %q contains non-ASCII characters\", directive, entry)\n\t\t\t}\n\t\t}\n\n\t\tisGlob := strings.HasSuffix(entry, \"*\")\n\t\tname := entry\n\t\tif isGlob {\n\t\t\tname = strings.TrimSuffix(entry, \"*\")\n\t\t}\n\n\t\t// Reject entries with '*' not at the trailing position.\n\t\tif strings.ContainsRune(name, '*') {\n\t\t\treturn nil, nil, nil, fmt.Errorf(\"%s: entry %q has '*' in an invalid position (only a trailing '*' is allowed)\", directive, entry)\n\t\t}\n\n\t\t// The name (without trailing '*') must contain at least one separator.\n\t\tif !strings.ContainsRune(name, sep) {\n\t\t\treturn nil, nil, nil, fmt.Errorf(\"%s: entry %q does not contain a %q\", directive, entry, sep)\n\t\t}\n\n\t\tcanonAllow := http.CanonicalHeaderKey(name)\n\t\tcanonDrop := http.CanonicalHeaderKey(strings.ReplaceAll(name, string(sep), \"-\"))\n\n\t\tif isGlob {\n\t\t\tprefixRules = append(prefixRules, aliasPrefixRule{\n\t\t\t\tallow: canonAllow,\n\t\t\t\tdrop:  canonDrop,\n\t\t\t})\n\t\t} else {\n\t\t\texactAllow[canonAllow] = struct{}{}\n\t\t\texactDrop[canonDrop] = struct{}{}","sourceCodeStart":388,"sourceCodeEnd":424,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/server.go#L388-L424","documentation":"Header alias allowlist entries support only a single trailing '*' as a wildcard. This error fires when '*' appears anywhere else in the name (after trimming a trailing '*'), e.g. a leading or middle asterisk. Such patterns are not supported by the precomputed exact/prefix matching structures the filter builds.","triggerScenarios":"An allowlist entry like \"*api.example.com\" or \"X-*Api*\" in the header alias config; only \"X_Api*\" style trailing globs are legal.","commonSituations":"Assuming full glob/fnmatch semantics when writing allowlist entries; porting wildcard patterns from other proxies.","solutions":["Move the wildcard to the end: use \"prefix*\" not \"*suffix\" or \"a*b\"","For leading-wildcard needs, enumerate explicit entries instead","Re-validate the config after the change"],"exampleFix":"// before\n\"-\": [\"*api.internal\"]\n\n// after\n\"-\": [\"x_api.internal\", \"stage-x_api*\"]","handlingStrategy":"validation","validationCode":"func validWildcard(entry string) error {\n    name := strings.TrimSuffix(entry, \"*\")\n    if strings.ContainsRune(name, '*') {\n        return fmt.Errorf(\"%q: '*' only allowed as trailing character\", entry)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat allowlist entries as 'exact name' or 'prefix*' only","Enumerate exceptions instead of inventing mid-string globs"],"tags":["caddy","headers","wildcard","config-validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}