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..9cd0a4f6b20 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go @@ -16,9 +16,6 @@ package fastcgi import ( "encoding/json" - "net/http" - "strconv" - "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" @@ -27,6 +24,9 @@ import ( "github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver" "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy" "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite" + "net/http" + "strconv" + "strings" ) func init() { @@ -312,12 +312,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 +363,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error rewriteMatcherSet := caddy.ModuleMap{ "file": h.JSON(fileserver.MatchFile{ TryFiles: tryFiles, + TryPolicy: tryPolicy, SplitPath: extensions, }), }