Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion echo.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,15 @@ func StaticDirectoryHandler(fileSystem fs.FS, disablePathUnescaping bool) Handle
// If the request is for a directory and does not end with "/" redirect to path which ends with "/"
p = c.Request().URL.Path
if fi.IsDir() && len(p) > 0 && p[len(p)-1] != '/' {
return c.Redirect(http.StatusMovedPermanently, sanitizeURI(p+"/"))
uri := p + "/"
// Keep the query string on the redirect target, as the query is still part of what the client asked
// for. `net/http.localRedirect` (used by `http.FileServer`) does the same, and dropping it here turns
// e.g. `GET /folder?v=2` into a permanent redirect to `/folder/`, which clients cache and then serve
// the wrong (or no) variant for.
if q := c.Request().URL.RawQuery; q != "" {
uri += "?" + q
}
return c.Redirect(http.StatusMovedPermanently, sanitizeURI(uri))
}
return fsFile(c, name, fileSystem)
}
Expand Down
9 changes: 9 additions & 0 deletions echo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,15 @@ func TestEcho_StaticFS(t *testing.T) {
expectHeaderLocation: "/folder/",
expectBodyStartsWith: "",
},
{
name: "Directory redirect preserves the query string",
givenPrefix: "/folder",
givenFs: os.DirFS("_fixture"),
whenURL: "/folder?foo=bar&baz=1", // no trailing slash
expectStatus: http.StatusMovedPermanently,
expectHeaderLocation: "/folder/?foo=bar&baz=1",
expectBodyStartsWith: "",
},
{
name: "Directory with index.html",
givenPrefix: "/",
Expand Down