{"record":{"id":"2445fd118120ff91","repo":"caddyserver/caddy","slug":"invalid-request-path","errorCode":null,"errorMessage":"invalid request path","messagePattern":"invalid request path","errorType":"http","errorClass":"caddyhttp.Error","httpStatus":400,"severity":"error","filePath":"modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go","lineNumber":168,"sourceCode":"// http3 requests have a negative content length for GET and HEAD requests, if that header is not sent.\n// see: https://github.com/caddyserver/caddy/issues/6678#issuecomment-2472224182\n// Though it appears even if CONTENT_LENGTH is invalid, php-fpm can handle just fine if the body is empty (no Stdin records sent).\n// php-fpm will hang if there is any data in the body though, https://github.com/caddyserver/caddy/issues/5420#issuecomment-2415943516\n\n// TODO: better default buffering for fastcgi requests without content length, in theory a value of 1 should be enough, make it bigger anyway\nfunc (t Transport) DefaultBufferSizes() (int64, int64) {\n\treturn 4096, 0\n}\n\n// RoundTrip implements http.RoundTripper.\nfunc (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {\n\tserver := r.Context().Value(caddyhttp.ServerCtxKey).(*caddyhttp.Server)\n\n\t// Disallow null bytes in the request path, because\n\t// PHP upstreams may do bad things, like execute a\n\t// non-PHP file as PHP code. See #4574\n\tif strings.Contains(r.URL.Path, \"\\x00\") {\n\t\treturn nil, caddyhttp.Error(http.StatusBadRequest, fmt.Errorf(\"invalid request path\"))\n\t}\n\n\tenv, err := t.buildEnv(r)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"building environment: %v\", err)\n\t}\n\n\tctx := r.Context()\n\n\t// extract dial information from request (should have been embedded by the reverse proxy)\n\tnetwork, address := \"tcp\", r.URL.Host\n\tif dialInfo, ok := reverseproxy.GetDialInfo(ctx); ok {\n\t\tnetwork = dialInfo.Network\n\t\taddress = dialInfo.Address\n\t}\n\n\tlogCreds := server.Logs != nil && server.Logs.ShouldLogCredentials\n\tloggableReq := caddyhttp.LoggableHTTPRequest{","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go#L150-L186","documentation":"The FastCGI transport rejects any request whose URL path contains a NUL byte (`\\x00`), returning HTTP 400 before dialing the backend. This is a security guard (Caddy issue #4574): PHP backends can be tricked into executing a non-PHP file as PHP code when null bytes survive into SCRIPT_FILENAME/PATH_INFO.","triggerScenarios":"A client request with `%00` URL-encoded into the path (e.g. `/uploads/avatar.png%00.php`) reaching a reverse_proxy route using the fastcgi transport. The check is `strings.Contains(r.URL.Path, \"\\x00\")` on the decoded path.","commonSituations":"Automated scanners probing php-cgi setups; crafted uploads where an attacker appends %00.php to a static file; occasionally broken clients that embed NULs in paths.","solutions":["No server fix is needed for legitimate traffic: reject/ignore such requests, they are malicious or broken","Ensure the fastcgi transport is only matched by routes you intend (e.g. `php_server` matcher), so static files are served by file_server instead","Keep Caddy updated so the null-byte guard is present in your deployment","Harden the PHP backend (php-cgi) independently, since this guard is defense-in-depth"],"exampleFix":"# before: fastcgi transport applied to everything\nexample.com {\n\troot * /srv\n\treverse_proxy localhost:9000 {\n\t\ttransport fastcgi\n\t}\n}\n# after: only route PHP index requests through fastcgi\nexample.com {\n\troot * /srv\n\tphp_server\n\tfile_server\n}","handlingStrategy":"validation","validationCode":"// server-side: nothing to guard; this is an intentional 400 for malicious paths.\n// In tests, assert the behavior:\nreq := httptest.NewRequest(\"GET\", \"/file.png%00.php\", nil)\n// expect 400 from the fastcgi transport route","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not attempt to bypass the check; it protects PHP backends from code execution","Scope the fastcgi transport narrowly (php_server matcher) so static files never reach it","Log 400s on %00 paths as security signal, not as errors to fix"],"tags":["fastcgi","security","http-400","php"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}