diff --git a/caddytest/integration/caddyfile_adapt/php_fastcgi_handle_response.caddyfiletest b/caddytest/integration/caddyfile_adapt/php_fastcgi_handle_response.caddyfiletest index 70a0780d110..bc785b0ac16 100644 --- a/caddytest/integration/caddyfile_adapt/php_fastcgi_handle_response.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/php_fastcgi_handle_response.caddyfiletest @@ -58,6 +58,7 @@ "{http.request.uri.path}/index.php", "index.php" ], + "try_policy": "first_exist_fallback", "split_path": [ ".php" ] diff --git a/caddytest/integration/caddyfile_adapt/php_fastcgi_matcher.caddyfiletest b/caddytest/integration/caddyfile_adapt/php_fastcgi_matcher.caddyfiletest index e5b331e31df..904c5b9736c 100644 --- a/caddytest/integration/caddyfile_adapt/php_fastcgi_matcher.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/php_fastcgi_matcher.caddyfiletest @@ -73,7 +73,8 @@ php_fastcgi @test localhost:9000 "{http.request.uri.path}", "{http.request.uri.path}/index.php", "index.php" - ] + ], + "try_policy": "first_exist_fallback" } } ] diff --git a/caddytest/integration/caddyfile_adapt/php_fastcgi_subdirectives.caddyfiletest b/caddytest/integration/caddyfile_adapt/php_fastcgi_subdirectives.caddyfiletest index a04d66fe42f..2c6222be375 100644 --- a/caddytest/integration/caddyfile_adapt/php_fastcgi_subdirectives.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/php_fastcgi_subdirectives.caddyfiletest @@ -59,6 +59,7 @@ php_fastcgi localhost:9000 { "{http.request.uri.path}/index.php5", "index.php5" ], + "try_policy": "first_exist_fallback", "split_path": [ ".php", ".php5" diff --git a/caddytest/integration/caddyfile_adapt/php_fastcgi_try_files_override_no_dir_index.caddyfiletest b/caddytest/integration/caddyfile_adapt/php_fastcgi_try_files_override_no_dir_index.caddyfiletest index dc4c3b886b1..203ab3b63b4 100644 --- a/caddytest/integration/caddyfile_adapt/php_fastcgi_try_files_override_no_dir_index.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/php_fastcgi_try_files_override_no_dir_index.caddyfiletest @@ -32,6 +32,7 @@ php_fastcgi localhost:9000 { "{http.request.uri.path}", "index.php" ], + "try_policy": "first_exist_fallback", "split_path": [ ".php", ".php5" diff --git a/modules/caddyhttp/fileserver/caddyfile.go b/modules/caddyhttp/fileserver/caddyfile.go index dbe2b2e5653..86c26ea3515 100644 --- a/modules/caddyhttp/fileserver/caddyfile.go +++ b/modules/caddyhttp/fileserver/caddyfile.go @@ -274,7 +274,7 @@ func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) tryPolicy = h.Val() switch tryPolicy { - case tryPolicyFirstExist, tryPolicyLargestSize, tryPolicySmallestSize, tryPolicyMostRecentlyMod: + case tryPolicyFirstExist, tryPolicyFirstExistFallback, tryPolicyLargestSize, tryPolicySmallestSize, tryPolicyMostRecentlyMod: default: return nil, h.Errf("unrecognized try policy: %s", tryPolicy) } diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go index c42907491a4..a8b4a303550 100644 --- a/modules/caddyhttp/fileserver/matcher.go +++ b/modules/caddyhttp/fileserver/matcher.go @@ -90,6 +90,7 @@ type MatchFile struct { // How to choose a file in TryFiles. Can be: // // - first_exist + // - first_exist_fallback // - smallest_size // - largest_size // - most_recently_modified @@ -415,13 +416,13 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) { } // setPlaceholders creates the placeholders for the matched file - setPlaceholders := func(candidate matchCandidate, info fs.FileInfo) { + setPlaceholders := func(candidate matchCandidate, isDir bool) { repl.Set("http.matchers.file.relative", filepath.ToSlash(candidate.relative)) repl.Set("http.matchers.file.absolute", filepath.ToSlash(candidate.fullpath)) repl.Set("http.matchers.file.remainder", filepath.ToSlash(candidate.splitRemainder)) fileType := "file" - if info.IsDir() { + if isDir { fileType = "directory" } repl.Set("http.matchers.file.type", fileType) @@ -429,8 +430,13 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) { // match file according to the configured policy switch m.TryPolicy { - case "", tryPolicyFirstExist: - for _, pattern := range m.TryFiles { + case "", tryPolicyFirstExist, tryPolicyFirstExistFallback: + maxI := -1 + if m.TryPolicy == tryPolicyFirstExistFallback { + maxI = len(m.TryFiles) - 1 + } + + for i, pattern := range m.TryFiles { // If the pattern is a status code, emit an error, // which short-circuits the middleware pipeline and // writes an HTTP error response. @@ -440,8 +446,15 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) { candidates := makeCandidates(pattern) for _, c := range candidates { + // Skip the IO if using fallback policy and it's the latest item + if i == maxI { + setPlaceholders(c, false) + + return true, nil + } + if info, exists := m.strictFileExists(fileSystem, c.fullpath); exists { - setPlaceholders(c, info) + setPlaceholders(c, info.IsDir()) return true, nil } } @@ -465,7 +478,7 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) { if largestInfo == nil { return false, nil } - setPlaceholders(largest, largestInfo) + setPlaceholders(largest, largestInfo.IsDir()) return true, nil case tryPolicySmallestSize: @@ -486,7 +499,7 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) { if smallestInfo == nil { return false, nil } - setPlaceholders(smallest, smallestInfo) + setPlaceholders(smallest, smallestInfo.IsDir()) return true, nil case tryPolicyMostRecentlyMod: @@ -506,7 +519,7 @@ func (m MatchFile) selectFile(r *http.Request) (bool, error) { if recentInfo == nil { return false, nil } - setPlaceholders(recent, recentInfo) + setPlaceholders(recent, recentInfo.IsDir()) return true, nil } @@ -708,10 +721,11 @@ var globSafeRepl = strings.NewReplacer( ) const ( - tryPolicyFirstExist = "first_exist" - tryPolicyLargestSize = "largest_size" - tryPolicySmallestSize = "smallest_size" - tryPolicyMostRecentlyMod = "most_recently_modified" + tryPolicyFirstExist = "first_exist" + tryPolicyFirstExistFallback = "first_exist_fallback" + tryPolicyLargestSize = "largest_size" + tryPolicySmallestSize = "smallest_size" + tryPolicyMostRecentlyMod = "most_recently_modified" ) // Interface guards diff --git a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go index 5dd19d21062..5ce6e3369f8 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go @@ -18,6 +18,7 @@ import ( "encoding/json" "net/http" "strconv" + "strings" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig" @@ -312,12 +313,18 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error if indexFile != "off" { dirRedir := false dirIndex := "{http.request.uri.path}/" + indexFile + tryPolicy := "first_exist_fallback" // if tryFiles wasn't overridden, use a reasonable default if len(tryFiles) == 0 { tryFiles = []string{"{http.request.uri.path}", dirIndex, indexFile} dirRedir = true } else { + if !strings.HasSuffix(tryFiles[len(tryFiles)-1], ".php") { + // use first_exist strategy if the last file is not a PHP file + tryPolicy = "" + } + for _, tf := range tryFiles { if tf == dirIndex { dirRedir = true @@ -357,6 +364,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error rewriteMatcherSet := caddy.ModuleMap{ "file": h.JSON(fileserver.MatchFile{ TryFiles: tryFiles, + TryPolicy: tryPolicy, SplitPath: extensions, }), }