{"record":{"id":"b9619a5edd8de2c0","repo":"caddyserver/caddy","slug":"v-the-default-header-modifier-can-only-be","errorCode":null,"errorMessage":"%v: the default header modifier ('?') can only be used on response headers; for conditional manipulation of request headers, use matchers","messagePattern":"(.+?): the default header modifier \\('\\?'\\) can only be used on response headers; for conditional manipulation of request headers, use matchers","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/headers/caddyfile.go","lineNumber":241,"sourceCode":"}\n\nfunc applyHeaderOp(ops *HeaderOps, respHeaderOps *RespHeaderOps, field, value string, replacement *string) error {\n\tswitch {\n\tcase strings.HasPrefix(field, \"+\"): // append\n\t\tif ops.Add == nil {\n\t\t\tops.Add = make(http.Header)\n\t\t}\n\t\tops.Add.Add(field[1:], value)\n\n\tcase strings.HasPrefix(field, \"-\"): // delete\n\t\tops.Delete = append(ops.Delete, field[1:])\n\t\tif respHeaderOps != nil {\n\t\t\trespHeaderOps.Deferred = true\n\t\t}\n\n\tcase strings.HasPrefix(field, \"?\"): // default (conditional on not existing) - response headers only\n\t\tif respHeaderOps == nil {\n\t\t\treturn fmt.Errorf(\"%v: the default header modifier ('?') can only be used on response headers; for conditional manipulation of request headers, use matchers\", field)\n\t\t}\n\t\tif respHeaderOps.Require == nil {\n\t\t\trespHeaderOps.Require = &caddyhttp.ResponseMatcher{\n\t\t\t\tHeaders: make(http.Header),\n\t\t\t}\n\t\t}\n\t\tfield = strings.TrimPrefix(field, \"?\")\n\t\trespHeaderOps.Require.Headers[field] = nil\n\t\tif respHeaderOps.Set == nil {\n\t\t\trespHeaderOps.Set = make(http.Header)\n\t\t}\n\t\trespHeaderOps.Set.Set(field, value)\n\n\tcase replacement != nil: // replace\n\t\t// allow defer shortcut for replace syntax\n\t\tif strings.HasPrefix(field, \">\") && respHeaderOps != nil {\n\t\t\trespHeaderOps.Deferred = true\n\t\t}","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/headers/caddyfile.go#L223-L259","documentation":"Returned while parsing the `header` Caddyfile directive: the '?' prefix marks a default value (set only if the header is absent) and is implemented via response matchers, which only exist for response headers. Using '?Field' in a request-header context (the `header` directive before a matcher separates request ops, or request header manipulation blocks) is a config error.","triggerScenarios":"Writing a Caddyfile like `header ?X-Custom value` where the tokens apply to the request header phase — i.e. the '?' field appears before any response-header section split, so respHeaderOps is nil at headers/caddyfile.go:241.","commonSituations":"Users wanting 'set X only if not already present' on requests forwarded upstream — not supported for request headers; porting response-header snippets to request-header blocks; misunderstanding that `header` mutates both request and response depending on syntax.","solutions":["Use the '?' modifier only where response headers are set, e.g. after a matcher or within `header <matcher> ?Field value` response context.","For conditional request headers, use a matcher (e.g. `@missing not header X-Custom *`) plus `request_header` inside a matched route.","Switch to explicit logic: set the request header unconditionally or use variable/placeholders to emulate defaults."],"exampleFix":"# before (invalid: default modifier on request header)\nheader ?X-Forwarded-Custom value\n# after (conditional via matcher + request_header)\n@no_custom not header X-Forwarded-Custom *\nrequest_header @no_custom X-Forwarded-Custom value","handlingStrategy":"validation","validationCode":"# Lint: forbid '?' modifiers outside response-header contexts\n# '?' fields are only valid in `header <matcher>` (response) usage; flag request_header blocks\ngrep -nE 'request_header[^{]*\\?' Caddyfile && { echo \"'?' default modifier used for request headers\"; exit 1; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember '?' = default-value modifier, response headers only.","For conditional request headers use matchers (not header_at_least_once semantics).","Run `caddy adapt` after writing header rules to confirm they parse."],"tags":["caddy","headers","caddyfile","config-parse","request-headers"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}