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 if

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Treat it as expected behavior: the '=NNN' token exists precisely to terminate file matching with that status; no code fix is needed.
  2. 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.
  3. If you did not intend a status fallback, remove the '=NNN' entry from try_files.
  4. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/6df258e5de07cf73. Report an issue: GitHub.