diff --git a/internal/neofs/tree.go b/internal/neofs/tree.go
index fe6d5212..592ca07d 100644
--- a/internal/neofs/tree.go
+++ b/internal/neofs/tree.go
@@ -573,22 +573,7 @@ func pathFromName(objectName string) []string {
 }
 
 func (c *TreeClient) GetLatestVersionsByPrefix(ctx context.Context, cnrID cid.ID, prefix string) ([]*data.NodeVersion, error) {
-	subTreeNodes, commonPrefix, err := c.getSubTreeByPrefix(ctx, cnrID, versionTree, prefix)
-	if err != nil {
-		return nil, err
-	}
-
-	var result []*data.NodeVersion
-	for _, node := range subTreeNodes {
-		latestNodes, err := c.getSubTreeVersions(ctx, cnrID, node.GetNodeId(), commonPrefix, true)
-		if err != nil {
-			return nil, err
-		}
-
-		result = append(result, latestNodes...)
-	}
-
-	return result, nil
+	return c.getVersionsByPrefix(ctx, cnrID, prefix, true)
 }
 
 func (c *TreeClient) determinePrefixNode(ctx context.Context, cnrID cid.ID, treeID, prefix string) (uint64, string, error) {
@@ -640,7 +625,7 @@ func (c *TreeClient) getPrefixNodeID(ctx context.Context, cnrID cid.ID, treeID s
 	return intermediateNodes[0], nil
 }
 
-func (c *TreeClient) getSubTreeByPrefix(ctx context.Context, cnrID cid.ID, treeID, prefix string) ([]*tree.GetSubTreeResponse_Body, string, error) {
+func (c *TreeClient) getSubTreeByPrefix(ctx context.Context, cnrID cid.ID, treeID, prefix string, latestOnly bool) ([]*tree.GetSubTreeResponse_Body, string, error) {
 	rootID, tailPrefix, err := c.determinePrefixNode(ctx, cnrID, treeID, prefix)
 	if err != nil {
 		if errors.Is(err, layer.ErrNodeNotFound) {
@@ -657,24 +642,44 @@ func (c *TreeClient) getSubTreeByPrefix(ctx context.Context, cnrID cid.ID, treeI
 		return nil, "", err
 	}
 
-	result := make([]*tree.GetSubTreeResponse_Body, 0, len(subTree))
+	nodesMap := make(map[string][]*tree.GetSubTreeResponse_Body, len(subTree))
 	for _, node := range subTree {
-		if node.GetNodeId() != rootID && hasPrefix(node, tailPrefix) {
-			result = append(result, node)
+		if node.GetNodeId() == rootID {
+			continue
 		}
+
+		fileName := getFilename(node)
+		if !strings.HasPrefix(fileName, tailPrefix) {
+			continue
+		}
+
+		nodes := nodesMap[fileName]
+
+		if !latestOnly {
+			nodes = append(nodes, node)
+		} else if len(nodes) == 0 || node.GetTimestamp() > nodes[0].GetTimestamp() {
+			nodes = []*tree.GetSubTreeResponse_Body{node}
+		}
+
+		nodesMap[fileName] = nodes
+	}
+
+	result := make([]*tree.GetSubTreeResponse_Body, 0, len(subTree))
+	for _, nodes := range nodesMap {
+		result = append(result, nodes...)
 	}
 
 	return result, strings.TrimSuffix(prefix, tailPrefix), nil
 }
 
-func hasPrefix(node *tree.GetSubTreeResponse_Body, prefix string) bool {
+func getFilename(node *tree.GetSubTreeResponse_Body) string {
 	for _, kv := range node.GetMeta() {
 		if kv.GetKey() == fileNameKV {
-			return strings.HasPrefix(string(kv.GetValue()), prefix)
+			return string(kv.GetValue())
 		}
 	}
 
-	return false
+	return ""
 }
 
 func isIntermediate(node *tree.GetNodeByPathResponse_Info) bool {
@@ -775,14 +780,18 @@ func formLatestNodeKey(parentID uint64, fileName string) string {
 }
 
 func (c *TreeClient) GetAllVersionsByPrefix(ctx context.Context, cnrID cid.ID, prefix string) ([]*data.NodeVersion, error) {
-	prefixNodes, headPrefix, err := c.getSubTreeByPrefix(ctx, cnrID, versionTree, prefix)
+	return c.getVersionsByPrefix(ctx, cnrID, prefix, false)
+}
+
+func (c *TreeClient) getVersionsByPrefix(ctx context.Context, cnrID cid.ID, prefix string, latestOnly bool) ([]*data.NodeVersion, error) {
+	prefixNodes, headPrefix, err := c.getSubTreeByPrefix(ctx, cnrID, versionTree, prefix, latestOnly)
 	if err != nil {
 		return nil, err
 	}
 
 	var result []*data.NodeVersion
 	for _, node := range prefixNodes {
-		versions, err := c.getSubTreeVersions(ctx, cnrID, node.GetNodeId(), headPrefix, false)
+		versions, err := c.getSubTreeVersions(ctx, cnrID, node.GetNodeId(), headPrefix, latestOnly)
 		if err != nil {
 			return nil, err
 		}
@@ -829,7 +838,7 @@ func (c *TreeClient) CreateMultipartUpload(ctx context.Context, cnrID cid.ID, in
 }
 
 func (c *TreeClient) GetMultipartUploadsByPrefix(ctx context.Context, cnrID cid.ID, prefix string) ([]*data.MultipartInfo, error) {
-	subTreeNodes, _, err := c.getSubTreeByPrefix(ctx, cnrID, systemTree, prefix)
+	subTreeNodes, _, err := c.getSubTreeByPrefix(ctx, cnrID, systemTree, prefix, false)
 	if err != nil {
 		return nil, err
 	}