From 2c918f27eea9eec303634329b083af94b43e119f Mon Sep 17 00:00:00 2001 From: yhirose Date: Wed, 14 Oct 2026 16:00:03 -0400 Subject: [PATCH] Redirect a directory request with a relative Location (#2630) * Redirect a directory request with a relative Location The trailing-slash redirect used the request path as-is, so a path starting with "//" produced a Location that clients read as a network-path reference to another host. Name only the last segment, percent-encoded and prefixed with "./", so the target always resolves against the request URL, whatever the mount point or any prefix a proxy put in front of it. * Shorten the relative redirect comment --- httplib.h | 6 +++++- test/test.cc | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/httplib.h b/httplib.h index 96c24e3..d6d2d1b 100644 --- a/httplib.h +++ b/httplib.h @@ -13869,7 +13869,11 @@ inline bool Server::handle_file_request(Request &req, Response &res) { detail::FileStat stat(path); if (stat.is_dir()) { - res.set_redirect(req.path + "/", StatusCode::MovedPermanently_301); + // Relative, so "//dir" cannot name another host; "./" keeps a ':' + // in the name from reading as a scheme. + auto name = req.path.substr(req.path.rfind('/') + 1); + res.set_redirect("./" + encode_path_component(name) + "/", + StatusCode::MovedPermanently_301); return true; } diff --git a/test/test.cc b/test/test.cc index 663d902..d6efb50 100644 --- a/test/test.cc +++ b/test/test.cc @@ -6664,7 +6664,14 @@ TEST_F(ServerTest, GetMethodDirMountWithoutTrailingSlash) { auto res = cli_.Get("/mount/dir"); ASSERT_TRUE(res) << "Error: " << to_string(res.error()); EXPECT_EQ(StatusCode::MovedPermanently_301, res->status); - EXPECT_EQ("/mount/dir/", res->get_header_value("Location")); + EXPECT_EQ("./dir/", res->get_header_value("Location")); +} + +TEST_F(ServerTest, GetMethodDirWithLeadingDoubleSlash) { + auto res = cli_.Get("//dir"); + ASSERT_TRUE(res) << "Error: " << to_string(res.error()); + EXPECT_EQ(StatusCode::MovedPermanently_301, res->status); + EXPECT_EQ("./dir/", res->get_header_value("Location")); } TEST_F(ServerTest, PostMethod303) {