caddyserver/caddy · info · caddyhttp.Error
%s
Error message
%s
What it means
Raised by MatchFile when a try_files pattern of the form '=NNN' (e.g. '=404') is evaluated and no earlier candidate matched. It is not a bug: parseErrorCode converts the '=NNN' token into a caddyhttp.Error carrying that status code, which Caddy then renders as the HTTP response. It only triggers when the numeric suffix parses (Atoi succeeds) and is in the range 100-999.
Source
Thrown at modules/caddyhttp/fileserver/matcher.go:539
return false, nil
}
setPlaceholders(recent, recentInfo.IsDir())
return true, nil
}
return false, nil
}
// parseErrorCode checks if the input is a status
// code number, prefixed by "=", and returns an
// error if so.
func parseErrorCode(input string) error {
if len(input) > 1 && input[0] == '=' {
code, err := strconv.Atoi(input[1:])
if err != nil || code < 100 || code > 999 {
return nil
}
return caddyhttp.Error(code, fmt.Errorf("%s", input[1:]))
}
return nil
}
// strictFileExists returns true if file exists
// and matches the convention of the given file
// path. If the path ends in a forward slash,
// the file must also be a directory; if it does
// NOT end in a forward slash, the file must NOT
// be a directory.
func (m MatchFile) strictFileExists(fileSystem fs.FS, file string) (os.FileInfo, bool) {
info, err := fs.Stat(fileSystem, file)
if err != nil {
// in reality, this can be any error
// such as permission or even obscure
// ones like "is not a directory" (when
// trying to stat a file within a file);
// in those cases we can't be sure ifView on GitHub (pinned to 50e54ee279)
Solutions
- Treat it as expected behavior: the '=NNN' token exists precisely to terminate file matching with that status; no code fix is needed.
- If clients should get a page instead of a bare status, put a real fallback file (e.g. /index.html) before the '=404' token in try_files.
- If you did not intend a status fallback, remove the '=NNN' entry from try_files.
- If the log noise is unwanted, lower the access/matcher log level or route these requests with a matcher that excludes missing assets.
Example fix
# before
try_files {path} {path}/ =404
# after (serve SPA fallback before falling back to 404)
try_files {path} /index.html =404 Defensive patterns
Strategy: validation
Validate before calling
// In config/tooling: detect '=NNN' try_files tokens and confirm the status is intended
for _, pattern := range tryFiles {
if strings.HasPrefix(pattern, "=") {
code, err := strconv.Atoi(pattern[1:])
if err != nil || code < 100 || code > 999 {
return fmt.Errorf("invalid status fallback %q", pattern)
}
// intentional: request will terminate with this status when reached
}
} Prevention
- Document in your config that '=NNN' in try_files is a terminal status fallback, not a file.
- Place real fallback files (e.g. /index.html) before '=404' so users get content, not bare statuses.
- Add monitoring that separates expected 404s from '=404' fallbacks if the noise matters.
When it happens
Trigger: A request reaches a route with `try_files {path} {path}/ =404` (or any '=NNN' token) where every preceding file candidate fails to stat, so the loop at matcher.go:444 reaches the '=NNN' pattern and returns its error.
Common situations: Standard SPA/static-site fallback config ('try_files {path} /index.html =404'); the error appears in logs when a client requests a missing asset and the intended behavior is to serve a 404. Also seen when someone writes '=404' expecting a redirect or rewrite instead of a status.
Related errors
- unknown try policy %s
- unsupported map key type in header match: %T
- unsupported value type in matcher input: %T
- unsupported element type in matcher input list: %T
- parsing browse template: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/6df258e5de07cf73.
Report an issue: GitHub.