caddyserver/caddy · warning · caddyhttp.Error
illegal ADS path
Error message
illegal ADS path
What it means
Returned by FileServer.ServeHTTP on Windows only: the request URL path contains a colon, which Windows interprets as an Alternate Data Stream (ADS) separator (e.g. 'file.txt:stream'). Serving such paths could bypass file hiding or expose hidden stream data, so Caddy rejects them with HTTP 400 before touching the filesystem.
Source
Thrown at modules/caddyhttp/fileserver/staticfiles.go:275
if sortOption != sortOrderAsc && sortOption != sortOrderDesc {
return fmt.Errorf("the second option must be one of the following: %s, %s, but got %s", sortOrderAsc, sortOrderDesc, sortOption)
}
default:
return fmt.Errorf("only max 2 sort options are allowed, but got %d", idx+1)
}
}
}
return nil
}
func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
if runtime.GOOS == "windows" {
// reject paths with Alternate Data Streams (ADS)
if strings.Contains(r.URL.Path, ":") {
return caddyhttp.Error(http.StatusBadRequest, fmt.Errorf("illegal ADS path"))
}
// reject paths with "8.3" short names
trimmedPath := strings.TrimRight(r.URL.Path, ". ") // Windows ignores trailing dots and spaces, sigh
if len(path.Base(trimmedPath)) <= 12 && strings.Contains(trimmedPath, "~") {
return caddyhttp.Error(http.StatusBadRequest, fmt.Errorf("illegal short name"))
}
// both of those could bypass file hiding or possibly leak information even if the file is not hidden
}
filesToHide := fsrv.transformHidePaths(repl)
root := repl.ReplaceAll(fsrv.Root, ".")
fsName := repl.ReplaceAll(fsrv.FileSystem, "")
fileSystem, ok := fsrv.fsmap.Get(fsName)
if !ok {
return caddyhttp.Error(http.StatusNotFound, fmt.Errorf("filesystem not found"))
}View on GitHub (pinned to 50e54ee279)
Solutions
- Change the client URLs to not contain colons in the path (use a different separator).
- If a colon is legitimately needed, encode it (%3A) only if downstream handling is safe — note the raw path check may still reject after decode; prefer renaming.
- On Windows, ensure sensitive files rely on `hide` plus this built-in guard rather than ADS obscurity.
Example fix
# before (client requests a path with ADS syntax) GET /report.txt:stream # after GET /report.txt (store the stream data as a separate file)
Defensive patterns
Strategy: validation
Validate before calling
// Upstream/reverse-proxy guard: rewrite or reject colon-containing paths before they reach clients
caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
if runtime.GOOS == "windows" && strings.Contains(r.URL.Path, ":") {
return caddyhttp.Error(http.StatusBadRequest, fmt.Errorf("illegal ADS path"))
}
return next.ServeHTTP(w, r)
}) Prevention
- On Windows deployments, never publish URLs with ':' in the path.
- Monitor 400s with 'illegal ADS path' as potential probing.
- Front-ending legacy apps that use colons? Map them to different URL shapes via rewrite.
When it happens
Trigger: Any request whose URL path contains ':' while running Caddy on Windows — e.g. GET /secret.txt:hidden or /C:/path — during normal request serving.
Common situations: Security probing/scanners on Windows deployments; clients sending URLs with absolute Windows paths; legitimate apps that use colons in path segments and must be re-designed when fronted by Caddy on Windows.
Related errors
- parsing browse template: %v
- parsing browse template file: %v
- parsing default browse template: %v
- invalid precompressed flag: %v
- getting module named '%s': %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/150386f3f2b2db13.
Report an issue: GitHub.