{"record":{"id":"aaf1de7538f7da6b","repo":"caddyserver/caddy","slug":"s-entry-q-contains-non-ascii-characters","errorCode":null,"errorMessage":"%s: entry %q contains non-ASCII characters","messagePattern":"(.+?): entry %q contains non-ASCII characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/server.go","lineNumber":394,"sourceCode":"\tallow string // canonical allowed form, e.g. \"Webhook_\" or \"Webhook.\"\n\tdrop  string // canonical hyphenated form, e.g. \"Webhook-\"\n}\n\n// provisionHeaderAliasAllowlist validates entries for a header-alias\n// allowlist (ExpectedUnderscoreHeaders or ExpectedDotHeaders) and builds\n// the precomputed maps and prefix rules used by the hot-path filter in\n// serveHTTP. sep is the separator the entries must contain ('_' or '.');\n// directive is the Caddyfile/JSON name used in error messages.\nfunc provisionHeaderAliasAllowlist(entries []string, sep rune, directive string) (exactAllow, exactDrop map[string]struct{}, prefixRules []aliasPrefixRule, err error) {\n\texactAllow = make(map[string]struct{}, len(entries))\n\texactDrop = make(map[string]struct{}, len(entries))\n\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}","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/server.go#L376-L412","documentation":"provisionHeaderAliasAllowlist validates header-alias allowlist entries (e.g. for the header-alias filter on requests) at server provisioning. It rejects any entry containing a byte >= 0x80 because Go's net/http parser returns 400 for non-ASCII header names, so such an entry could never match a real request. The %s is the directive name ('_' or '.' separated variants) and %q the offending entry.","triggerScenarios":"Configuring the header alias allowlist with a header name containing UTF-8 or any high-byte characters, e.g. copied from documentation with a smart quote or non-breaking space.","commonSituations":"Copy-pasting header names from rich-text docs/websites that insert invisible Unicode characters, or editing configs in editors that save smart quotes.","solutions":["Rewrite the entry using only ASCII letters, digits, '-', '_' and '.'","Hex-dump the config region to find hidden non-ASCII bytes (e.g. 'grep -P \"[\\x80-\\xff]\" config')","Disable editor features that produce smart quotes when editing configs"],"exampleFix":"// before\n\"-\": [\"X-Custom Header\"]  // contains U+00A0 non-breaking space\n\n// after\n\"-\": [\"X-Custom_Header\"]","handlingStrategy":"validation","validationCode":"func allASCII(entries []string) error {\n    for _, e := range entries {\n        for i := 0; i < len(e); i++ {\n            if e[i] >= 0x80 {\n                return fmt.Errorf(\"entry %q has non-ASCII byte at %d\", e, i)\n            }\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Edit configs with smart-quotes/dashes disabled","Scan config files for high bytes: grep -P '[\\x80-\\xff]' Caddyfile","Copy header names from RFC-style plain-text sources"],"tags":["caddy","headers","config-validation","ascii","provisioning"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}