{"record":{"id":"eb79b1aeee050a05","repo":"caddyserver/caddy","slug":"rfc9112-forbids-empty-host","errorCode":null,"errorMessage":"rfc9112 forbids empty Host","messagePattern":"rfc9112 forbids empty Host","errorType":"http","errorClass":"HandlerError","httpStatus":400,"severity":"error","filePath":"modules/caddyhttp/server.go","lineNumber":696,"sourceCode":"\tif len(r.Method) > 32 {\n\t\tif s.shouldLogRequest(r) {\n\t\t\tif c := s.accessLogger.Check(zapcore.DebugLevel, \"rejecting request with long method\"); c != nil {\n\t\t\t\tc.Write(\n\t\t\t\t\tzap.String(\"method_trunc\", r.Method[:32]),\n\t\t\t\t\tzap.String(\"remote_addr\", r.RemoteAddr),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\treturn HandlerError{StatusCode: http.StatusMethodNotAllowed}\n\t}\n\n\t// RFC 9112 section 3.2: \"A server MUST respond with a 400 (Bad Request) status\n\t// code to any HTTP/1.1 request message that lacks a Host header field and to any\n\t// request message that contains more than one Host header field line or a Host\n\t// header field with an invalid field value.\"\n\tif r.ProtoMajor == 1 && r.ProtoMinor == 1 && r.Host == \"\" {\n\t\treturn HandlerError{\n\t\t\tErr:        errors.New(\"rfc9112 forbids empty Host\"),\n\t\t\tStatusCode: http.StatusBadRequest,\n\t\t}\n\t}\n\n\t// Drop headers whose names contain `_` or `.`: once FastCGI/CGI/FrankenPHP etc.\n\t// rewrites `-` to `_` (and PHP additionally folds `.` to `_` when registering\n\t// $_SERVER keys), an underscore or dot alias collides with the legitimate\n\t// hyphenated header and can bypass `forward_auth copy_headers`\n\t// (GHSA-f59h-q822-g45g, GHSA-49wc-4hcv-v58q).\n\t//\n\t// The two allowlists (ExpectedUnderscoreHeaders, ExpectedDotHeaders) are\n\t// otherwise independent: each only keeps headers spelled with its own\n\t// character, and either allowlist actively drops the plain-hyphenated\n\t// variant of its entries.\n\tfor k := range r.Header {\n\t\thasUnderscore := strings.ContainsRune(k, '_')\n\t\thasDot := strings.ContainsRune(k, '.')\n","sourceCodeStart":678,"sourceCodeEnd":714,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/server.go#L678-L714","documentation":"loadECHConfig could not read the metadata file (meta.json) and the error was not fs.ErrNotExist (missing files are tolerated with a warning and recreated later), so it attempted cleanup — deleting the config folder — and that delete failed too. This is the double-failure variant for the metadata load path.","triggerScenarios":"storage.Load of meta.json returns a non-ErrNotExist error (permission denied, backend I/O error) AND storage.Delete of the folder fails (read-only storage, outage).","commonSituations":"Permissions changed on meta.json only; partially failed storage where reads error; read-only volumes blocking the cleanup delete.","solutions":["Check both wrapped errors to identify the storage fault (permissions vs connectivity).","Restore read/write storage access, remove the reported folder if needed, restart.","Note: a simply-missing meta.json is fine (warns and recreates) — only non-ErrNotExist load errors trigger this path."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":"// When implementing custom storage, return fs.ErrNotExist for missing keys so Caddy's tolerant path engages:\nfunc (s customStorage) Load(ctx context.Context, key string) ([]byte, error) {\n    b, err := s.get(key)\n    if os.IsNotExist(err) { return nil, fs.ErrNotExist }\n    return b, err\n}","tryCatchPattern":"if err != nil && !errors.Is(err, fs.ErrNotExist) { // real failure: fix storage, delete named folder, restart } — remember missing meta.json alone is fine (warned, recreated).","preventionTips":["Custom storage modules must map 'missing key' to fs.ErrNotExist exactly.","Keep meta.json writable and delete-capable.","Do not change permissions on individual files within config folders."],"tags":["caddy","caddytls","ech","storage","metadata","cleanup"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}