diff --git a/src/util/UriExtract.cxx b/src/util/UriExtract.cxx
index 048e363a9..cf1ab64c4 100644
--- a/src/util/UriExtract.cxx
+++ b/src/util/UriExtract.cxx
@@ -81,7 +81,7 @@ uri_is_relative_path(const char *uri) noexcept
 }
 
 std::string_view
-uri_get_path(std::string_view uri) noexcept
+uri_get_path_query_fragment(std::string_view uri) noexcept
 {
 	auto ap = uri_after_scheme(uri);
 	if (ap.data() != nullptr) {
@@ -101,6 +101,23 @@ UriWithoutQueryString(std::string_view uri) noexcept
 	return Split(uri, '?').first;
 }
 
+std::string_view
+uri_get_path(std::string_view uri) noexcept
+{
+	auto path = uri_get_path_query_fragment(uri);
+	if (path.data() == nullptr || path.data() == uri.data())
+		/* preserve query and fragment if this URI doesn't
+		   have a scheme; the question mark may be part of the
+		   file name, after all */
+		return path;
+
+	auto end = path.find('?');
+	if (end == std::string_view::npos)
+		end = path.find('#');
+
+	return path.substr(0, end);
+}
+
 /* suffixes should be ascii only characters */
 std::string_view
 uri_get_suffix(std::string_view _uri) noexcept
diff --git a/src/util/UriExtract.hxx b/src/util/UriExtract.hxx
index f757c943f..e4bb90fad 100644
--- a/src/util/UriExtract.hxx
+++ b/src/util/UriExtract.hxx
@@ -25,8 +25,16 @@ bool
 uri_is_relative_path(const char *uri) noexcept;
 
 /**
- * Returns the URI path (including the query string) or nullptr if the
- * given URI has no path.
+ * Returns the URI path (including query and fragment) or nullptr if
+ * the given URI has no path.
+ */
+[[gnu::pure]]
+std::string_view
+uri_get_path_query_fragment(std::string_view uri) noexcept;
+
+/**
+ * Returns the URI path (excluding query and fragment) or nullptr if
+ * the given URI has no path.
  */
 [[gnu::pure]]
 std::string_view